浏览器解析jsx_Nanachi H5 转译方案原理解析

aa6e58c8d499ed582684c6f7057be62d.gif

36ebc8ef75cbcc218886bf961dd1a46d.png

作者介绍

邵裕东,2018年3月入职 Qunar,现任平台事业部大前端技术中心前端开发工程师,负责公司移动端框架 Hy、QRN 的开发维护,Nanachi 小程序多端转译框架开发。追求前端工程化,喜欢做一切有意义的事。

2d67ef2b3d0c5eba8e14258aa06c5c94.gif

方案初探

Nanachi

Nanachi 是 Qunar 开发的多端小程序转译框架。使用 React 语法开发,实现一处编写、多端运行,极大地提高了我们的开发效率。目前 Qunar 已经使用该框架成功上线各大小程序、快应用平台,Nanachi 成为公司小程序开发标准技术框架。

a6d9a817bf445a44126929cb3e864693.png

Nanachi 官网:https://rubylouvre.github.io/nanachi/index.html

运行方式

npm install nanachi-cli -g # 全局安装 Nanachi 转译器 nanachi init yourprojectname # 初始化模板项目 cd yourprojectname && npm install nanachi watch # 编译项目,默认编译微信,其他平台需要传入对应参数,如 watch:ali、watch:bu、watch:h5...

dc8e3f06e0f237dff6c3b75a461db2c0.png

编译分为 build 和 watch 模式,watch 模式会自动监听代码变化重新构建。

H5 编译的 watch 模式会在本地启动 webpack-dev-server,方便实时调试。

874e65c921ab3ec2a493d2875251a710.png

H5 方案意义

相较传统 H5 方案优势

用 Nanachi 的方式编写 React SPA 应用,与传统浏览器开发相比,有以下优势:

  • 编写一次,多端运行,适配成本极低。

  • 丰富的组件库,兼容微信的组件实现方式。

  • 封装了浏览器端的小程序 API(如:提示框、打电话、震动、本地存储、网络请求…),功能上与原生体验接近,开发方便。

  • 不需要关心路由跳转问题,节约开发成本。

  • 封装好了基础的 Webpack 打包配置,无特殊需求可实现零配置打包;同时暴露 Webpack 配置接口,可自由定制。

让 Nanachi 适用于更多场景开发

如果已使用 Nanachi 开发了小程序应用,H5 方案适用于以下场景:

  • 可以快速上线一套与小程序业务逻辑一致的 touch 端应用,抢占浏览器流量入口。

  • 小程序平台目前层出不穷,可以通过转出的 touch 端应用套 Webview 壳快速抢占某个新小程序平台入口,待 Nanachi 官方支持该平台时,也可快速迁移至原生小程序应用。

  • 小程序开发需要打开 IDE,等待原生代码编译。可以先通过 H5 方案实现基本样式、功能,最后阶段编译成原生代码适配各平台差异功能,提高开发效率。

技术细节、难点解析

编译时方案

H5 方案代码编译过程分为三个阶段:

8c6ea2ed9b505f1f7c210555cc79d7d9.png

React SPA 应用源码编译出 Webpack 产物的过程大多数前端开发都很熟悉了,这里我们着重介绍从源码到生成中间产物的过程。

小程序中存在很多标签,在浏览器端没有对应的原生实现,我们首先要对这些标签进行转译。

对一些基础标签,会进行直接的标签替换:

1ca8412636ba2bbd7a321b3c46897f93.png

对一些复杂的组件,我们实现了一套组件库,schnee-ui,用来抹平浏览器和微信原生标签的差异。

schnee-ui 官网:https://qunarcorp.github.io/schnee-ui/index.html

编译时我们会自动导入组件依赖:

07548e08c865b80cb7f9e00c5b5ac1de.png

开发过小程序的同学知道,小程序分为 App、Page、Component 三级结构,对于页面(Page)组件,我们对代码进行如下处理:

35ccc2c22ac25432f868cb2ff3a94877.png

dynamicPage 是我们内部实现的高阶组件,负责每个页面的生命周期钩子调用、标题栏、tab 栏的渲染、下拉刷新功能的实现、过场动画等等。

对于 App 组件,代码处理如下:

0d2ebabc42b2827569dca9cc918134de.png

在 App 中挂载 PageWrapper 组件,这是我们整个应用的容器,主要职责是渲染页面栈。

运行时方案

抹平 API、组件(包括原生 titleBar、tabBar)

我们在浏览器端实现了一套和微信小程序兼容的 API,并且同时支持 Callback 和 Promise 的方式接收回调信息。

07bfd629ecb16b8769251ce7df170c23.png

组件的设计方面,我们也保持和微信小程序写法完全一致。除了一些基础的按钮、选择器、switch 开关等,还包括了一些通用的复杂业务组件,比如城市选择器、轮播图、日历等。

680d87d3c513a9b2f68d94529948cc27.png

实现了浏览器端没有的标题栏和 tab 栏:

46de70c9bb3ce225b085646d64cb29dd.png

统一生命周期

React 方案里缺少一些小程序端的生命周期,我们会在浏览器端模拟实现,在特定的时机触发对应的钩子:

5c96c5474b40c7dc01d90b5ed8101ed3.png

页面堆栈管理

小程序的页面堆栈行为是各平台管理的,到浏览器端我们就需要自己维护一套页面管理方案,同时要和微信端保持一致。我们会在内部定义一个__currentPages 数组,用来存储页面的 React 实例,并实现自己的路由方法,管理这些实例。

11ae22dae22604f78d42d0a9810fe9e7.png

同时模拟了小程序的最大页面数概念,对微信来说目前能保持最多 10 个页面。我们每次调用 navigateTo 方法时会检查当前页面栈长度是否达到最大值,达到则先推出数组的第一个元素,再存入新页面实例。

技术难点

路由管理

路由管理的实现过程主要遇到两方面的问题:

  • 如何更新地址栏

    需要在调用路由方法时同时改变地址栏,对 navigateTo 我们调用 history.pushState,redirectTo 调用 history.replaceState 方法,实现地址栏的更新。

  • 如何保证调用 API 返回与浏览器原生返回事件行为一致

    我们知道浏览器原生返回操作会触发 popstate 事件,所以可以监听这个事件然后调用我们内部的 API 方法,达到行为一致。设计方案如下: 

311c587608891c95b504ac481b658e36.png

样式作用域

小程序的样式是天然具有局部作用域的,到浏览器端样式会变为全局样式,必然出现样式污染问题,解决方案有以下几种:

  • 业务自定义命名空间,缺点:工作量大、可靠性低,最先被排除掉。

  • Css Module 方案,传统 touch 端开发的流行局部样式解决方案,缺点:业务需要修改代码适配,成本高。

  • Web Component 方案,也是微信小程序的局部样式解决方案,优点:无适配问题,业务无感知;缺点:浏览器端有兼容性问题。

  • 编译时为标签添加特定 hash 属性(参考 VUE scoped 样式方案):优点:业务无感知,无兼容性问题。

我们采取的是最后一种方案,计算样式文件和它对应的js文件共同父级目录的 MD5 值,作为关联两者的唯一 hash,分别添加到jsx标签和 style 文件的标签选择器上,实现局部样式效果。

d94bda49dc61c1643296349c6f421d6f.png

总结

Nanachi 作为多端转译框架,已经成为 Qunar 小程序的开发标准,其跨端特性极大地提高了开发效率。对 H5 端的支持也让我们可以快速上线与小程序功能一致的 touch 应用。相较传统 React 开发,我们维护了与微信高度一致的组件和 API,开发者不用再去引入各种第三方组件库来实现一些复杂功能;同时路由行为也由框架层维护,进一步减轻开发负担;最后可以实现零配置打包上线,也支持业务自定义各种 Webpack 配置,简单灵活。

最后欢迎大家试用 Nanachi,帮我们提 issue、PR,我们会第一时间解决或提供支持。

项目地址:https://github.com/RubyLouvre/anu

b4a0402f7e25c71a691558247112aa5f.gif

9143b442d2a8f7e6282585b5d2efdb2f.png

71db6ecf64427396a31b82af8bae4da4.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值