Kbone原理解析
官方介绍
“Kbone 是一个致力于微信小程序和 Web 端同构的解决方案。”
Web端框架基本原理
首先我们来看下普通Web端框架,以Vue框架为例,一份Vue模板对应一个组件,在代码构建阶段编译成调用Dom接口的JS函数,执行此JS函数就会创建出组件对应的Dom树,从而渲染到浏览器页面上。
然而,小程序是双线程的,并没有Dom树的概念,逻辑层和视图层完全分离,逻辑层是一个纯粹的JSCore,开发者可以编写JS脚本,但是无法直接调用Dom/Bom的api, 没有任何浏览器相关的实现。
在小程序中,视图层和逻辑层的交互是通过数据和时间驱动的。
因此,要实现跨端同构,问题是:怎么将web端代码转为小程序代码?
业界常规做法
目前业界流行的第三方跨端框架们,常规做法都是:静态编译兼容。
原理是把代码语法分析一遍,然后将其中的模板部分翻译成对应的跨端需求的模板(微信小程序、支付宝小程序、H5、APP等)。
静态编译最大的局限性是无法保证转换的完整性,因为Vue模板和WXML模板的语法并不是直接对等的,Vue的特性设计也和小程序的设计无法划等号,这自然就导致了部分Vue特性的丢失。
比如像Vue中的v-html指令、ref获取Dom节点、过滤器等就通通用不了。
除了Vue自身的特性外,一些原本依赖Dom/Bom接口的Vue插件页无法使用,例如Vue-Router。
Kbone的做法
Kbone是通过提供 适配器 的方式来实现同构,即运行时兼容,而非静态编译。
Kbone的适配器核心包含两个部分:
miniprogram-render: 仿造Dom/Bom接口,构造仿造Dom树;
miniprogram-element: 监听仿造Dom树变化,渲染到页面,同时监听用户行为,触发事件。
仿造Dom树和浏览器的运行时对比:
仿造Dom树:
利用内置组件和自定义组件的自引用来进行递归,创建组件树。
如图,自定义custom-dom为递归自引用组件:
递归的终止条件是遇到特定节点、文本节点或者children空节点。
在创建出组件树后,将Dom节点和自定义组件实例进行绑定,以便后续的Dom更新和操作。
kbone这里还对节点数进行了优化:
如果一个dom节点对应一个自定义组件的话,就会创建很多自定义组件,这样会很浪费开销,这里做了子树的合并,也就是说3层才创建一个自定义组件,节省开销。
优化前:17个dom=17个自定义组件;
优化后:17个dom=4个自定义组件,蓝色那个是单节点,会合并到上面的树;
dom 子树作为自定义组件渲染的层级数是可以通过配置传入,理论上层级越多,使用自定义组件数量越少,性能也就越好。
一棵很大的 Dom 树,一次性 setData 到视图层,可能会超过 setData 的大小限制(1024kB),拆分成多棵子 Dom 树,然后分批的 setData 到视图层,可以节省开销。
事件监听
小程序的事件是视图层到逻辑层的通讯方式,事件绑定在组件上,当被触发时,就会执行逻辑层中对应的事件处理函数。
小程序的捕获冒泡是在视图层view端,因此逻辑层在整个捕获冒泡流程中各个节点接收到的事件不是同一个对象,小程序事件的捕获冒泡和阻止冒泡等操作必须在WXML模板中生命,无法使用接口实现。
为了能够让web端和小程序端的事件系统行为一致,kbone除了仿造了一份Dom树外,也把整个事件系统仿造了一份,即在仿造Dom树上进行捕获冒泡。
当自定义组件监听到用户的操作后,就将事件发往仿造Dom树,后续自定义组件监听到的同一个事件的冒泡就直接忽略。
当触发改节点,仿造Dom树接收到事件后,再进行捕获和冒泡,让事件在各个节点触发。
Kbone的优势
- 支持多个前端框架:Vue、React、Preact 等
- 支持更为完整的前端框架特性: Vue 中的 v-html 指令、Vue-router 插件等
- 提供了常用的 dom/bom 接口
- 可以使用小程序本身的特性: live-player 内置组件、分包功能等
- 提供一些 Dom 扩展接口:getComputedStyle 接口等
Kbone实践
脚手架kbone-cli
官方已经提供了kbone-cli可以用来快速开发:
用npm全局安装kbone-cli
可以根据自己的技术栈选择不同的开发模板:React/Vue/Omi/Preact
然后就可以愉快的进行开发啦~
生成的demo项目结构如下:
demo中包含了多页跳转、vue-router、vuex等的使用示例,以及mp-webpack-plugin的配置示例。
对于多页面的应用,在 Web 端可以直接通过 a 标签或者 location 对象进行跳转,但是在小程序中则行不通。同时 Web 端的页面 url 实现和小程序页面路由也是完全不一样的。
Demo示例对比
其中,有一部分两端差异的业务逻辑功能,也给出了3中不同的解决方案:
利用vue-improve-loader,在构建时对dom树节点进行删减,在需要提出的节点加上check-reduce属性
利用reduce-loader,将业务中不需要被打包的代码进行去除,使用行内loader和环境变量来判断
使用样式隐藏,即设置不需要显示的节点样式为 display:none
其他问题
在实际开发中,还会碰到一些细节,例如:
- 多页面开发:修改webpack和mp-webpack-plugin配置
- 小程序内置组件: 部分用html标签代替,其他用wx-component + behavior标签
- 小程序自定义组件:修改mp-webpack-plugin配置,补充wxCustomComponents字段,将自定义组件放入组件根目录,使用自定义组件
- 自定义app.js和app.wxss:监听app的生命周期,修改webpack配置补充app.js的构建入口,修改插件配置的generate.app字段,补充app.js
- 扩展dom/bom对象和API:使用 window.$$extend追加方法
- 代码优化:用reduce-loader做体积精简,dom树精简用vue-improve-loader
- 区分环境实现不同功能:process.env.isMiniprogram
更新迭代
kbone由于目前在快速发展期,更新迭代非常迅速,以下特性是对比了8月份的版本和11月份版本,可以看出已经解决了近2/3的问题。
小程序技术选型
详细了解了kbone之后,我们来分析下小程序技术框架到底应该怎么选?
kbone & 小程序原生
- 已有web版,需要小程序版:kbone
- 跨平台需求(web + 小程序):kbone
- 对性能特别苛刻 or 追求稳定 or 要用最新功能:小程序原生
- 页面节点数量特别多( 1000 节点以上),且对渲染性能要求高:静态模板转义方案(第三方框架:mpvue/taro等)
第三方框架
- MpVue :不推荐再用了,坑越来越多,内部也表示之后不会投入太多维护
- WePY 1.7.x :不推荐再用了:1.7.x 的版本在最初的设计上的缺陷导致遗留了很多比较严重的问题
- WePY 2.0:现在还是 alpha 阶段,内外部有一些小程序在跑,体验和反馈还可以。但依然 issue 比较多。害怕踩坑的也不推荐使用
- Taro: 也还是有不少问题,但相对来说应该是比 mpVue 和 WePY 更稳定一点
- Uni-app:mpvue的衍生版,跨端 (官方示例有6端) 支持的很好,在H5端实现了一套微信模拟器,可以尝试,是目前唯一支持app端的商用方案,有独立的编辑器HBuildX
- Chameleon: 统一的多态协议,渐进式跨端,提供脚手架命令工具,规划比较宏大
- Omi :基于Taro完成的框架,kbone有支持omi的模板
- Nanachi: 基于react的编译期间的转义框架
总结
没有跨端需求,只需要微信小程序 ==> 小程序原生
web端转小程序 or 两端 or 想要尝鲜 ==> kbone
多端 or Vue 技术栈 ==> uni-app
多端 or React 技术栈 ==> taro
不介意学习新技术栈 ==> wepy 2.0 or chaemeleon
写在最后
小程序在非常快速的更新迭代,就算是原生框架也还是有一些坑的,因此没有哪种框架是百分之百完美,需要根据业务具体需求以及自身技术栈偏好来进行选择。
文章中提及到的部分第三方框架只是参考了官方文档,没有逐个一一尝试,有问题麻烦指出,鞠躬~~
uni-app早就完成了全新重构,目前已经和mpvue没啥关系了,说”uni-app是mpvue的衍生版”并不准确。
目前尝试的路线是小程序原生+flutter for iOS android H5
大佬好啊,请问文章可以授权转载到公众号「高级前端进阶」吗,会注明作者和原文地址,感谢
组件树猴 是一个新物种吗?