项目背景
随着 58 业务版图不断壮大,带来的技术挑战是怎么在业务融合的过程中避免重复建设,提升开发以及多端落地效率,集团孵化了内部项目 58-rn,目前为止已经在组件化、开发流程优化、发布平台建设以及线上监控预警等环节拥有一定积累。同时,Taro 是一款优秀的跨端开发框架,在 58 众多产品线中都有使用,例如二手房、新房等。Taro 3 发布后暂不支持 React-Native 平台,于是我们向社区提交了一份实现草案,希望把 58 在 React-Native 上的技术积累分享到社区,同时也从社区对 Taro 的共建上获益。
特性
- 更纯粹的 React Native 支持,社区生态对接更加简便;
- 更好的 source-map 支持,开发调试体验大幅提升;
- 更加完善的样式写法支持;
- 与 Taro 3 React 框架一致的运行时标准;
- 更丰富的 API 与组件,原生依赖支持按需引入;
- 高稳定高性能,源码 TypeScript 开发、单元测试覆盖全;
核心关注点
鉴于 Taro 良好的可扩展架构演化,适配 React-Native 变得更加容易。本文总结一些我们在设计上的新思考,希望能够抛砖引玉。
Webpack or Metro?
Taro 目前的核心流程如下所示:
(出处:https://nervjs.github.io/taro/blog/2020-07-01-taro-3-0-0/)
我们做出了一点小改变,采用 metro 直接生成 bundle 文件;这与 WEB 端的处理类似。广义上理解,针对小程序各端的处理也基本类似,只不过中间多了一步:先编译成目标平台的代码,然后再用小程序 IDE 编译发布。当然它与前两者还是有些差异:目标平台的中间代码包含 Taro 运行时,调试起来并不是非常直观;而前两者直接生成最终打包资源,所以天然地支持 source-map 。 这一改变带来的好处:
-
更好支持项目调试;打包资源中的运行时错误通过 source-map 直接与项目工程代码对应,定位问题更直观;
-
Metro 更加贴合 React-Native 的打包场景:通过多级缓存以及 hasteFS 让打包速度更快;
-
React-Native 社区基于 Metro 的打包优化方案对接起来更容易;
现在情况变成了:WEB 和 React-Native 直接打包;小程序则使用端平台插件机制进行扩展,生成目标平台代码,需要二次编译才能发布。框架图后半部分更新后看起来像这样:
“零“ 成本适配 React-Native 平台
在讨论方案之初,我们的目标之一:Taro3 项目只要升级框架版本就可以作为 React-Native 项目运行起来,无需对 Taro 项目模板进行任何修改;这保证了业务逻辑代码以非常低的成本在 React-Native 端进行复用。当然我们没法保证应用的运行效果完全符合预期;例如 CSS 标准中,子元素可以继承其父元素的样式,这在 React-Native 中并不适用;为此,我们支持编写针对React-Native 平台的专属样式。另外,React-Native 的样式定义是 CSS 标准的子集,所以我们在编译时会对 React-Native 不支持的样式给出提示以及修复建议;这样开发者就可以逐渐熟练编写兼容多端的样式。 最后,如果开发者仍然希望使用 React-Native/cli 的话,只需要修改 metro 配置就可以自由切换。目前,仅支持基于 React 编写的应用“零”成本扩展到 React-Native 端。
促进社区融合
目前 Taro 和 React-Native 社区都主要以 WEB 开发者为主,但是侧重点略有不同。Taro 主打 WEB 和小程序的融合,兼容多个应用开发框架,更偏向于小程序生态;React-Native 主打 React 生态,降低学习成本(learn once, write anywhere)。 Taro 1 和 2 做了大量工作来支持 React-Native,但是在开发体验以及 API、组件适配程度上还需要进一步完善。我们在不影响 Taro 核心的情况下尽量解决这些痛点;例如,利用 expo 重构组件库模块 @tarojs/components-rn,重写了 tab-bar 功能等。并开发独立壳应用,本地不用配置原生开发环境也可以进行 React-Native 开发。对于 Taro 社区的开发者而言,应用可以 ”无缝“ 扩展到 React-Native 平台;另一方面,React-Native 社区的开发者可以把 Taro 项目当作是 React-Native 应用的另一种项目模板,开发体验”无缝“衔接。
新增组件 & API
最后但也是非常重要的一点,就是提升 Taro 组件和 API 规范在 React-Native 端的覆盖度。要真正做到一套代码,多端运行,核心在于组件 & API 规范的覆盖度。我们支持基础型组件(例如容器类、表单类等)的同时,也逐渐在进一步完善其它类型的组件以及 API。
tabbar & Navigator
tab 结构是最常用的页面布局之一,仅仅通过 app 配置,就能生成一个多 tab 的 React-Native 页面。
媒体组件
对于富有表现力的应用,总需要视频、音频以及相机组件的加持;我们已经把这些都集成进来了。
扫描二维码
当下扫码经济如火如荼,基于开发者调研结果我们新增了 API scanCode,希望解决实际项目开发中的痛点。
长列表
React-Native 已经有非常成熟的方案来优化长列表,在此基础上我们实现了组件 VirtualList。当开发者碰到长列表卡顿时,不妨尝试这个新组件。
还有更多
PickerView 、MovableView等。
设计思路
现在分三种主要场景去阐述我们是怎么实现“无缝“集成 React-Native 平台的。
启动 Dev Server
运行命令 yarn dev:rn 启动 Metro bundle 服务器时,
-
@tarojs/cli 通过编译平台扩展 presets/platforms/rn.ts,启动@tarojs/rn-runner
-
@tarojs/rn-runner,它把 Taro 项目配置以及命令行参数转换成 MetroServer 所需的配置,并与用户自定义的 metro 配置合并(可选),然后启动 MetroServer
访问 bundle 文件
MetroServer 实质还是一个 HTTP 服务器,当发起 /index.bundle 请求时,
-
如果是初次请求,IncrementalBundler 会初始化 DependencyGraph,挂载 hasteFS 的 change 事件,监听文件变更;如果是非初次请求,IncrementalBundler 会生成当前请求模块相关的变更集
-
在模块处理过程中,@tarojs/rn-transformer 会针对入口文件和页面文件进行特殊处理,主要是支持 tab-bar、app 以及页面配置
-
@tarojs/rn-style-transformer 会针对样式进行处理,主要完成样式语言 Sass/Less/Stylus 的覆盖
-
等变更集中的模块更新之后,重新让当前模块经过 Serializer 序列化成字符串返回
Bundle执行
当我们启动 React-Native 原生 Android 应用时(iOS类似),
-
CatalystInstanceImpl::runJSBundle 执行 bundle 的入口
-
@tarojs/runtime-rn 调用 AppRegister.registerComponent 注册页面,并提供 API(@tarojs/taro-rn)和组件(@tarojs/components-rn)支持
如何使用?
如果你已经想使用 Taro3 开发 React-Native 应用的话,步骤非常简单:
# 注意:@tarojs/cli 最新版本还未发布,体验版在标签 canary 下
$ yarn add @tarojs/cli@canary
# 创建并初始化 Taro 项目
$ npx taro init <projectName>
# 设置环境变量DEVTAG,安装体验版相关依赖:
$ export DEVTAG=@canary
# 启动 Dev Server,此处会显示监听端口号;支持 -p 选项手动指定端口
$ cd <projectName> && yarn dev:rn
下载壳应用(https://github.com/NervJS/taro-native-shell/tree/0.63.2),然后构建并安装到模拟器或真机上(以 android 为例):
# 安装依赖
$ yarn
# 构建并启动应用
$ yarn android -- --no-packager
如果缺少原生开发环境的话,直接下载应用安装包即可:android 地址(https://share.weiyun.com/xB4OJiBw)。
依次执行 开发者菜单 -> Bundle Location 修改 bundle 的地址为 http://localhost:\ (端口号在执行 yarn dev:rn 时会在终端中输出)就可以看到入口页面了。
React-Native 相关调试以及常见使用问题请参考详细文档(https://taro-docs.jd.com/taro/docs/3.x/react-native/)。
如何升级?
3.x - 升级 @tarojs/cli 最新版本;把项目中 Taro 相关依赖包升级到最新版本;然后运行 yarn dev:rn, 此时会自动安装 React-Native 相关依赖。
2.x - 先删除 package.json 中 已存在的 React-Native 相关依赖项(@tarojs/rn-runner、@tarojs/runtime-rn、@taorjs/components-rn、@tarojs/router-rn 以及 @tarojs/taro-rn),然后按 3.x 的升级步骤即可。
如果升级过程中碰到问题,请通过末尾附上的渠道与我们沟通进行解决。
未来规划
更高的 API、组件适配度
API、组件适配注定是一项耗时费力的长期工程,我们会继续努力提升针对 React-Native 平台的适配度。另外,也会关注组件性能问题,让应用的操作体验更加流畅。
动画
动画是 React-Native 持续优化的重要场景,我们会充分释放其在这方面的优势,提升 Taro 应用的交互体验。
完善开发流程
目前我们主要解决了 Taro 扩展到 React-Native 端开发环节中的核心问题,后续会进一步支持项目的发布流程,集成热更新、CI/CD 等功能。
注意:本文归作者所有,未经作者允许,不得转载