参考文档:
Taro的官方文档, taro-docs.jd.com/taro/blog/2…
mpvue: github.com/mpvue/mpvue…
从时间节点来回顾整个多端方案框架的进程, 我们会发现多端方案的愿景非常简单: 使用一套DSL , 可以“write once, run everywhere”,
以下为市面上流行的小程序开发框架
目前小程序多端方案最终对外的使用方式可以分为:
- 类Vue风格框架
- 类React风格框架
- 自定义DSL框架
小程序的多端方案, 从技术上实现可分为三大类, 其中第三种(运行时结合编译时方案),是目前的主流技术
1. 类Vue风格框架编译原理
Vue DSL静态编译
Vue的设计风格和各小程序的设计风格更加接近, 所以Vue DSL静态编译方案相对容易
如下图
- 因为小程序基本都可以接受H5环境中的CSS, 因此style部分基本可以直接平滑迁移。
- template转换为.wxml文件, 需要进行HTML标签、模版语法的转换。如mpvue、uniapp等, 这一部分依赖AST技术, 不再展开详细介绍,可实操 astexplorer.net/
Vue运行时编译
静态编译主要是Vue单文件组件的template编译过程, 而script部分处理会更加困难。
Vue中: 通过响应式监听数据变化, 触发视图修改, 对应到小程序中, 多端方案需要做的就是监听数据变化, 调用setData()方法, 触发小程序渲染层变化。
以mpvue为例: mpvue 就是fork了一份Vue.js代码, 内置了Vue runtime能力, 同时添加了小程序平台的支持
以生成微信小程序为例:
1. 初始化: 微信小程序平台规定, 小程序页面中需要有一个Page()方法。
// 业务方代码 new Vue() {}
// 经过多端方案转换 Vue.init = () => {
Page() // 手动执行微信小程序平台的Page(), 完成初始化处理。
}
2. 数据变动:
Vue基于响应式, 对数据进行监听, 在数据改动时, 新生成一份虚拟节点VNode, 然后对比新旧两份虚拟节点, 找到diff, 调用小程序API—setData()方法, 更新一份最新的数据, 至于渲染部分就交给小程序架构处理。
3. 其他
网易考拉的megalo, 对数据结构进行扁平化处理, 再调用setData()时获得更好的性能。
2. 类React风格框架编译时和运行时结合方案
类React风格的小程序多端方案, 存在很多难处理的问题, 其中之一,就是如何将过于灵活性的JSX转换成小程序模版?因为JSX过于灵活, 单纯基于AST技术很难进行一对一转换。
仔细看以下代码, 利用JSX表达能力实现的Render Prop模式, 如果不将代码运行, 很难计算出需要表达的视图结果。
function CompParent({children, ...props}) {
return typeof children === 'function' ? children(props) : null
}
function Comp() {
return (
<CompParent>
{props => <div>{props.data}</div>}
</CompParent>
)
}
目前为止, 针对“JSX”转换, 类React风格的小程序多端方案, 大致分为两种处理方式:
1. 强行静态编译型: 代表有Taro1/2, 去哪儿的Nanachi
2. 运行时处理型: 代表有: Taro Next, 蚂蚁的Remax
强行静态变异需要业务使用方在编写代码时,规避掉一些难以在编译阶段处理的动态化写法, 说到底就是在使用过程中, 对JSX写法有限制
下图是Taro对此进行的说明
多端框架结合React设计理念
小程序多端方案 > image2022-3-15_13-22-14.png")
- react-reconciler: 维护virtualDOM, 负责diff算法, 无缝衔接patch
如图, 在初始化阶段以及第一次mount时, 我们通过setData()方法初始化小程序。具体是通过递归VNodeData这个数据结构, 根据不同的type渲染出不同的小程序模版。接着, 在数据发生变化时, 我们通过React reconciler阶段的计算信息, 以及自定义配置的HostConfig衔接函数, 更新数据, 并通过setData()方法触发小程序的渲染更新。
简单了解Taro Next实现
由上图可看到: Taro提供的taro-react包,是用来连接React reconciler和taro-runtime的, 主要负责以下两点:
- 实现HostConfig配置
- 实现render函数(类似于React DOM.render方法), 创建Taro DOM Tree的容器
3. 选型(以下内容数据待更新)
3.1 性能优化方向
一般来说,编译时做的事情越多,下的功夫越大,也就意味着运行时越轻量,负担越小,因此性能也就会更好。
- 框架包size(下图数据不准确, 待更新)
- 数据更新粒度 数据更新阶段, 小程序的setData()所负载的数据是一直是重要的优化方向。 如数据负载的扁平化处理和增量处理, 都是常见的优化手段。
3.2 未来发展方向
- 工程化方案。 小程序多端需要有一体的工程解决方案。兼顾关键环节的可插拔性。 如可以跟webpack等工程化工具深度融合, 并对外提供服务。
- 框架方案。目前以Vue和React为主。但是Fullter和Angular等小众框架也应当受到重视。考虑到投入产出比,如果小程序多端团队难以面面俱到地支持这些框架和新 DSL,那么交给社区寻求支持,也是一个思路。比如,Taro 团队将支持的重点放在 React/Vue,而快应用以及 Flutter 和 Angular,暂且交给社区来适配和维护。
- 跟进web发展。在运行时,小程序多端方案一般需要在小程序逻辑层中运行 React 或者是 Vue 的运行时版,然后通过适配层,实现自定义渲染器。这就要求设计开发者需要跟进 Web 发展及 Web 框架的运行时能力,且实现适配层。
- 渐进增强能力。无论是和 Web 兼容互通还是多种小程序之间的差异磨平,对于多端方案来说,很难从理论上彻底实现“write once,run evrywhere”。因此,这就需要框架级别上实现一套渐进增强能力。这种能力,可以是语法或 DSL 层面的暂时性妥协/便利性扩展,也可以通过暴露全局变量,进行不同环境的业务分发。比如腾讯开源的 OMIX 框架:OMIX 有自己的一套 DSL,但整体保留小程序已有的语法。在小程序已有语法之上,OMIX 还进行了扩充和增强,比如引入了 Vue 中比较有代表性的 computed。