本文由 InfoQ 整理自京东零售平台业务中心高级前端工程师朱天健在 GMTC 全球大前端技术大会(深圳站)2021 的分享《Taro 在多端浪潮下的选择与挑战》。
多端研发对于当今时代的前端开发来说是个绕不过去的话题,为了解决这些问题,各大公司的开源项目推出的开源框架不在少数,而 Taro 从 2018 年开源至今已经三年有余,在 GitHub 上收获了超过 3W 的 Starred,在开源后帮助很多团队和开发者的同时,我们也通过与社区内万余活跃用户的交流和反馈摸索前进的方向,与诸多开发者一起共建社区,和 Taro 一起成长。
我们到底需要一个怎样的多端解决方案呢?以 Taro 的视角来说,我们基础的前提是希望多端不会成为开发者的障碍,特别是在小程序生态整体上呈现欣欣向荣之势时,我们期待整个生态都可以继续成长,与开发者们相互成就。
近几年来,每隔一段时间就会有很多新的小程序平台出现,带来了更多流量渠道的同时,也给开发者创造了很多难题。
小程序平台
产品同学可以很轻易地喊出“我全都要!”,那么我们作为开发可以吗?不论是 Web 、React Native 端,还是各类小程序平台,在开发过程中都势必会牵扯很大的精力。特别是平台方很多时候也没有足够的时间和精力照顾到所有开发者的开发体验,而同样对于很多跨端框架的开发者来说,适配一个全新的平台也势必不是一件容易的事情。
跨平台开发是小程序生态持续扩张的重要力量,同时也使小程序成为互联网生态快速拓展的核心之一。这就是为什么我们期待中的小程序生态需要大家共同建设,每一个新成员的加入,带来的都不仅仅是流量,更是新鲜的活力,这些都会促进整个生态的繁荣。
但是,这里就不得不提到但是了!!!代价又是什么呢?
面对多个平台同步开发的过程中,我们往往会遇到很多问题,比如:为了同步多端能力,我们往往很难快速响应业务需求;如果采用原生开发,每增加一个平台,研发成本都是指数级增长;而开发完成并不意味着任务结束了,维护这些多端同构又具有差异化的代码的困难程度可想而知。
所以对于开发者来说需要的无非是多端一体化的开发体验;希望框架在开发维护舒适的同时,还可以拥有卓越的性能。插件化同样是十分重要的能力,对于很多项目不需要的能力完全可以自主选择;而对于更多社区的开发者群体来说,如果有特殊的需求,可以通过定制化的插件去实现,而不需要等待官方响应。
本质上我们需要面向开发、面向开源社区中的各个团队以及其中诸多开发者开放框架的能力。
我们希望可以让开发者自由地选择自己喜欢的框架,不论你是喜欢 React、Vue 还是更多其他的解决方案,都可以不受限制地去使用这样一个多端解决方案去实现自己的创意,这才是开源的精神,也是我们做开源项目的目的。
当然这也就有了 Taro 向社区内所有开发者提供的开放式跨端跨框架解决方案。
没有什么是一蹴而就的,对于 Taro 团队来说,我们面临的很多问题也是一样的。虽然刚刚我们提到了很多,但这些内容也是我们一组一个脚印慢慢实现地。而在最早期的时候,Taro 还没有这么开放,它只支持 React Like 的方式去写小程序,而且还是一个重编译时的框架。
就像这个架构图显示的一样,我们通过 AST 解析开发者的 React Like 代码,转换成对应端的小程序代码,通过适配层就可以完成所有小程序的适配。虽然一句话就可以讲完我们所有的工作,但是其中的困难很多都难以想象,我们发现维护框架的成本逐渐超出了我们可以接受的极限,所以我们选择向前再迈出一大步。
不得不说,插件化架构给我们了很大帮助,我们将所有可以拆分出来的东西悉数拆分,几乎所有的能力都是通过插件化的方式提供,最终也就形成了 Taro 3.x 的框架。
在 Taro 3.x 对整体框架调整之后,大大降低了适配新框架的成本,通过对齐组件和生命周期我们就可以在 Taro 的生态中引入一个新的框架。
当然在这个架构中 Web 、React Native 和小程序端的实现略有不同。在小程序中,我们需要注入 DOM、BOM 并提供对应框架的渲染器,最终通过 Webpack 打包给开发者使用;Web 端则是通过 WebComponent 提供了标准化的组件,同时结合标准的路由体系模拟了所有的 API 提供给开发者;React Native 中也是同样的逻辑,通过 Metro 将标准化的组件和 API 打包给开发者使用。
插件化架构帮助我们引入了一个新概念——端平台插件,它帮助我们将每一个端的能力拆分开,插件通过提供编译时的模板、组件、编译配置,加上运行时所需的组件、API 和渲染器,将它们分别注入 Taro CLI 和 Runtime 中对应的生命周期,Taro 就能够转译成对应端的代码。
以微信小程序插件 @tarojs/plugin-platform-weapp 为例,我们通常会按照如下方式组织项目文件:
├── src 源码目录
| ├── index.ts 插件入口
| ├── program.ts 编译时入口
| ├── template.ts 模板处理逻辑
| ├── runtime.ts 运行时入口
| ├── runtime-utils.ts 运行时依赖工具
| ├── apis.ts API 相关处理
| ├── apis-list.ts API 列表
| ├── components.ts 组件列表
| └── components-react.ts 给 React 使用的组件类型
├── types 类型
├── index.js 编译时入口
├── tsconfig.json
├── rollup.config.json
├── package.json
└── README.md
通过继承基类 TaroPlatformBase 实现端平台的编译,然后在插件入口函数中调用上述自定义平台类的编译接口:
// index.ts
import Weapp from './program'
export default (ctx) => {
ctx.registerPlatform({
name: 'weapp',
useConfigName: 'mini',
async fn (arg) {
// 调用自定义平台类的 start 函数,开始端平台编译
const program = new Weapp(ctx, config)
await program.start()
}
})
}
runtime.ts 是我们运行时的入口文件, Webpack 编译时会把它注入到 app.js 中进行引用。
// runtime.ts
import { mergeReconciler, mergeInternalComponents } from '@tarojs/shared'
import { hostConfig, components } from './runtime-utils'
mergeReconciler(hostConfig)
mergeInternalComponents(components)
使用 mergeReconciler 函数把自定义的 hostConfig 合并到全局 Reconciler 中;
使用 mergeInternalComponents 函数把自定义组件信息 components.ts 合并到全局 internalComponents 组件信息对象中。
// runtime-utils.ts
export * from './components'
export const hostConfig = {}
// 抽取 runtime-utils.ts 是为了方便其它插件引用
参考 @tarojs/plugin-platform-weapp 插件我们可以快速注册一个其他端的小程序插件,横向拓展自定义所需的组件、API、渲染器和运行时。当然也可以此为基础纵向拓展一个平台插件,这样可以快速完成相似的小程序平台插件开发。
当然整个插件架构的设计并不仅仅是用于构建端平台插件,各类插件都能很好地完善 Taro 生态,提供更好的开发体验,社区内很多开发团队或者个人都提供了非常有意思的插件用于各种研发场景,欢迎大家体验。
想要适配框架对于现在的 Taro 来说,并非是一件非常困难的事情,因为 Taro 提供了自己模拟的浏览器环境。
作为浏览器环境的基础,BOM 和 DOM 相关的 API 是必不可少的,这也是供各类前端框架在小程序中运行的根本。
Taro 通过模拟这些 API 构建了自己的运行时环境,将所有的事件和节点方法在小程序环境中以对齐 Web 标准的形式呈现。
以基础的 click 事件为例,当小程序中 tpl_text 节点触发事件时,框架会通过 Taro DOM Tree 提供的 getElementById 方法找到 text 上的 onClick 方法找到相应的事件并触发。
同样冒泡机制也是事件中重要的组成部分,所以在父级节点 tpl_view 上的 onClick 方法同样会被触发,这时就需要通过调用 stopPropagation 方法来阻止事件的传播。
在完成浏览器环境的构建之后,兼容各类框架相对就容易很多,通过 Taro 提供的标准事件将框架的生命周期和小程序关联到一起,就能够让开发者在使用时有更顺滑的体验。
首先要做的就是挂载入口,将构建好的 DOM 树在小程序触发 onLaunch 事件时挂载上去,当然每个框架有自己的写法,比如 React 中是 render Vue 中是 $monut 方法。
紧接着我们需要挂载页面,在页面触发 onLoad 时为页面注入 ref 等信息,并将页面渲染呈现。而每个页面都会有属于自己的 pageId,这也使得我们可以将所有的生命周期和每个页面一一对应,在合适的时机触它们。
<html>
<head>
...
</head>
<body>
<div id="app">
<Page ref={pageId} />
...
</div>
</body>
</html>
当生命周期对齐,页面只要渲染出来就可以了,当然这里每个框架会有些不同。以 React 为例,ReactDOM 作为渲染器,功能非常丰富,但包体很大而且并非所有都在小程序中需要用到,选择使用 react-reconciler 自定义渲染器精简是个很容易做出的决断,加上 hostConfig 注入对应的依赖, taro-react 就完成了。
在运行时中通过 createReactPage 就可以创建 Taro DOM Tree 并通过 setData 和模板交互数据完成渲染。
在 Taro 中使用 Vue 也是一样的,通过调用 createVuePage 创建 DOM Tree 之后就可以实现打通整个流程。
当然大家如果使用 Taro 支持新框架时,可能会遇到一些问题,比方说 DOM/BOM 相关的 API 缺失或者功能错误等等,可以通过 issue 反馈,我们会动态拓展。
对于 Taro 来说端有很多,小程序端、React Native 端、Web 端、快应用端,其中很多都是社区帮助完成适配的。在这里我们以小程序端为例,Taro DOM Tree 在渲染的过程中根据不同的小程序平台其实是会有所不同的,但都是基于 template 组件提供的渲染方法。
通过模板的一一对应,我们就能完成所有组件的渲染,但不同平台的模板功能是差异化的,有的支持递归,而有的并不支持,这也就分成了两种不同的小程序类别。比方说阿里、头条、百度的小程序都支持递归,我们可以让模板自我调用来避免重复。
渲染方案 3-2
而微信、京东、QQ 小程序不支持,我们则会创建新的模板。不支持递归的模板模式当然有可能会存在一些问题,比方说模板文件过大,但我们可以通过设置 baseLevel 配置合理优化递归层级数;同样因为自定义组件样式会有影响,避免嵌套也是一个优化的方案。
Taro 的支持跨端一直以来都走在业内的前沿,虽然大家可能更熟悉的是我们支持微信、京东、阿里、百度、头条和 QQ 小程序,以及 Web 、React Native 和快应用端这些,但其实 Taro 社区内提供的平台远不止于此。
不知道你是否尝试使用 Taro 编译快手、芒果、飞书,甚至是支付宝 IOT 小程序呢?这些新增的平台有些是官方的小程序平台提供,也有很多其他公司的团队或者个人开发者根据自身的业务需求开发提供的,当然也期待可以在社区中看到更多大家编写的 Taro 平台插件。
说起小程序和 H5 的生态融合,不知道大家会想到什么呢?可能也会困惑什么是生态融合?Taro 为什么要做生态融合?又是怎么做到生态之间的融合呢?
Taro 已经支持了这么多小程序平台,同时也支持了 H5 端,那么是不是大家都使用了 Taro 框架,小程序端和 H5 的生态就可以融合起来呢?显然不会是。
在各个不同的小程序生态之中沉淀了大量专属的小程序插件,如果开发者想要使用 Taro 的同时直接利用这些专属的原生小程序能力可以么?这显然不会有任何问题能形成阻碍。
那么原生小程序能不能通过 Taro 转换成 H5 应用呢?通过 Taro 的 convert 指令你就可以完成原生到 Taro 项目的过程,Taro 可以帮助你转换到任何平台去使用。
npx @tarojs/cli convert
那么这就够了么?我有一个原生的项目可不可以使用 Taro 编写的组件呢?有同学可能会说 Taro 可以编译小程序插件的,你导出插件就可以在原生应用使用 Taro 。但实际上 Taro 同时还提供了 blended 指令,可以将 Taro 项目编译到指定平台,供原生小程序或者其他端的应用集成使用。
taro build --type weapp --watch --blended
除此之外还有吗?Taro 可不可以直接在小程序使用 web 组件呢?
仔细思考一下,这件事情完全是可以的!所以我们新增了一个插件 @tarojs/plugin-html 来完成这件事情。
实现这个插件笼统地说,我们的任务主要划分为三个方向,分别将标签名、属性、组件映射就可以了,当然实际上还是会遇到很多问题,但这是有价值的,特别是它在社区内有很高的呼声。最重要的原因就是我们可以通过这个插件直接在小程序中使用 web 端的各种工具,特别是一些组件库,我们可以直接在 Taro 中使用而不需要特别考虑跨端的问题。
比如 Ant Design、 Vant 以及 WeUI 我们都提供了 demo 给到社区可以直接使用,这将进一步打通 Web 端和小程序端的生态壁垒,同样很多老项目都可以直接在小程序端直接运行,也会是一个很大的便利。
当然这并非毫无限制,比方说 DOM API 会有一些差异:
小程序部分 API 不支持同步调用
小程序部分组件 API 需要通过 Context 调用
同样一些特殊的用法,包括部分标签节点,在使用时也存在一定的差异:
不支持在 DOM Tree 之外插入节点(React 的 Portal,Vue3 的 Teleport 都是不支持的)
ReactDOM 功能精简(Taro 使用 react-reconciler 自定义调试器)
小程序不支持部分样式或 CSS 选择器(默认宽高并非原图宽高)
小程序不支持 svg
更多限制细节可以参考官网上的相关文档。
Taro 也能支持鸿蒙吗?这是我在很多社区的交流群中听到最多的一句话,但其实 Taro 支持鸿蒙是可行性非常高的,毕竟 OpenHarmony 的 JS UI 语法和小程序的相似度很高。所以其实早在鸿蒙第一次曝光时双方的团队就有过很多深入地交流,并探讨过相关的可行性,当然一直到近期我们才有足够的人力去完成鸿蒙平台的适配。
Taro 和华为以及开放原子基金会都多有合作,但是总结下来就是 Taro 即将加入 OpenHarmony 并成立 CrossPlatformUI Sig,为鸿蒙提供跨端能力的支持。
对于 Taro 来说,我们同样可以通过插件的形式适配鸿蒙,将它作为一个平台横向拓展就可以完成鸿蒙的接入。
当然为了抹平鸿蒙和其他平台的差异,我们需要针对处理的问题还有不少,比方说支持 React&Vue 语法,支持标准的组件和 API 等等,支持语法通过编写框架的运行时就可以实现,而组件和 API 则需要通过 OH 提供的能力来实现。
最终就可以看到我们写的代码可通过 webpack 打包成应用,在前端框架层通过 Taro 提供的运行时与 UI 视图交互数据和事件,加上 OH 提供的基础能力就可以为鸿蒙端适配渲染。
Taro for Harmony 相关的能力将会作为 v3.5 版本的特性发布,目前 canary 版本已经发布,欢迎大家踊跃尝试,我们也会和社区诸多开发者们一同持续完善相关的能力,并提供支持。
最后的最后,关于 Taro 还在做的事情其实还有很多,我们在这里简单地总结展望一下。
首先是就 Taro for Harmony,刚刚其实也有提到,我们很快会发布正式版本,目前正在邀请社区的同学进行内测当中,正式版本发布时间,我们预计会在 2022 年 3 月份,大家感兴趣可以关注,当然也可以通过小助手加入 Taro 的鸿蒙交流群,获取更多的信息。
跨平台测试一直以来都备受社区开发者们的关注,我们其实在内部已经有两套方案持续迭代当中,只是由于很多原因一直没有开源出来给大家使用。
测试沙箱作为一套自研的方案,可以完全适配全平台的小程序和 Web 端的逻辑测试,通过模拟小程序的 DOM 结构、生命周期和 API 来辅助开发者完成小程序相关的能力测试;在 Web 端则是通过解析生成代码的 AST 获取应用和 Taro 实例,结合 Jsdom 模拟实际的浏览器环境,达成和小程序一致的测试体验。
另一个方案 TIga 则相对更真实,通过 puppeteer 和小程序官方提供的接口在浏览器环境或者小程序官方平台提供的测试环境中完成 UI 测试,目前支持了 Web 端和微信小程序,在内部项目的使用中也广受好评。两个方案各有侧重,希望有一天能够开源,与社区的开发者见面。
Cloud IDE,也就是 Tide 项目,也倾注了我们团队很多成员的心血,通过内置的 Taro 研发流程,很好地解决了在小程序开发过程中的诸多痒点,比方说模板、插件管理,多端配置差异,版本管理等等工程化的问题。
在大型小程序项目中产品、设计、研发和测试之间的团队协同;同时通过内置的小程序模拟器,避免了调试中窗口反复切换开发者工具模拟器;小程序测试上线流程、线上环境监控……这些目前在内部使用的反响都不错,很好地帮助我们解决了很多团队内的问题。
文末回答两个大家关心的问题:
Taro3 性能优化的方向
其实会上也有同学提到关于 Taro 在 3.4 版本推出支持了 preact,确实不论是 Taro-react 还是支持 preact 都是 Taro 在压缩包体积方案中做出的尝试,同时我们也打算通过 wasm 来提升 Taro3 在小程序中的性能。虽然 Taro3 目前还没有官方的 benchmark,但也在筹备当中,敬请期待。
Taro3 的后续版本规划与 Taro4
对于 Taro4 目前还无可奉告,但是在 Taro3 的后续版本特性,可以关注我们对外开放的 Task List,并对当前的 Feature Request 投票,对于很多开发中的新特性在这里都能看到,当然大家如果什么想法也可以在这里提交,如果希望参与一些特性的研发工作,也欢迎大家提交 PR。
2019 年作为讲师出席第二届软件绿色联盟「构生态·建未来」开发者大会,2020 年受邀参加第四届 TLC 腾讯直播大会。作为 Taro 框架的核心开发成员,主要负责多端组件库及 API 相关的研发工作,同时负责开源社区管理机器人群控系统;Tide 项目的核心开发成员,主要负责 tide-site 和 cloud ide 相关的研发,第三方调用协议和相关接口,以及 Taro 相关的生态插件等。
2022 年 6 月 10-11 日,GMTC 北京站将与您再度相约!本次大会以“业务至上,效率为王”为主题,涵盖前端性能优化、IoT 动态应用开发、TypeScript、移动端性能与效率优化等 15 个前沿技术专题,点击底部【阅读全文】直达大会官网,更多精彩内容持续打磨上线中。大会门票 7 折限时优惠,立减 1440 元!感兴趣的同学联系票务经理:17310043226。
本文分享自微信公众号 - 凹凸实验室(AOTULabs)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。