导读
由 58 前端团队主导的 Taro 3 适配 React Native 工作已完成有一段时间了。目前发布了多个体验版,也将在3月底迎来正式版。基于 Taro 的良好架构演变,适配 React Native 的方案的也做了较大调整,本文将主要介绍 Taro 3 适配 React Native 运行时相关的详细设计与实现。
背景
Taro 已经进入3.0 时代,相对于 Taro 1/2 来说,采用重运行时架构,可以让开发者能够获得完整的 React/Vue 等框架开发体验,因此,我们在设计 Taro3 React native 的方案时,也是基于运行时方案,增加 taro-runtime-rn 包来适配 React Native 端,使得 Taro 标准的 React 代码可运行在 React Native 端,让开发者可以低成本的扩展到 React Native 端。
方案设计
Taro 3.0 是近乎全运行时方案,在设计整个架构时,从浏览器的角度去思考,无论是开发框架是什么, React 也好, Vue 也罢,最终代码经过运行之后,都是调用浏览器的 BOM/DOM 的 API,因此,对于小程序端,Taro 团队增加 taro-runtime 包,在这个包中实现一套高效、精简版的 DOM/BOM API, 当运行在小程序端时,也有一套高效的 DOM/BOM API,从而实现了跨框架开发方案。 详细内容参考( https://mp.weixin.qq.com/s/5pdUD9YNojgvZBSve5-2EA )
在设计 Taro3 React Native 方案之初,我们希望可以与小程序端标准较为一致的方案,对比了两种方案:
以上两种方案,如果采用基于小程序的方案,会存在以下问题:
因此,我们采用第二种方案,更好的贴近 React Native 生态,通过编译和运行时适配,让 Taro3 的 React 代码可以方便的扩展到 React Native 端。
详细设计
Taro3 React Native 整体方案的设计思路:基于 Taro 源码,利用 Metro 打包直接生成 jsBundle,通过编译和运行时适配到 Taro 的写法。
(https://mp.weixin.qq.com/s/-7G7NMHX8ol99QxkswFOxg)
直接基于源码去打包运行时适配,如何做适配,需要适配哪些内容?
Taro3 React Native 是整体方案是利用 Metro 基于 Taro 源码打包。
Metro 是针对 React Native 的 JavaScript 模块打包工具,接收一个入口文件和打包配置,将项目中所有依赖打包在一个或多个js文件。
打包过程会分为三个阶段:
对于 Taro 写法的支持,我们在 Transformation 转化阶段,通过自定义的 taro-rn-transformer 与 taro-rn-style-transformer 对 Taro 的代码进行转换。
Taro 3 React Native中,运行时方案主要包含三个模块 ,各个模块之间的关系:
对于 Taro 运行时的适配的内容,如图所示:
在 React Native 中,AppRegistry 是所有 React Native 应用的 JS 入口,通过 AppRegistry.registerComponent 方法注册根组件,若有多个页面,在根组件中建立对应导航系统。
在 Taro 中,入口是按照小程序方案来定义,要运行在 React Native 端,需将这些配置转换为 React Native 相关的配置,生成可运行在 React Native 的入口文件。
Taro 中入口文件:
//app.config.tsexport default { pages:[ 'pages/index/index', 'pages/index/about' ], window:{ backgroundTextStyle: 'light', .... }, tabBar:[...]}
//app.tsxexport defalut class App extends Component{ ... render(){ return this.props.children }}
我们实现方案的基本思路是: 读取 app.config ,获取到对应的页面信息,将页面在入口文件引入,建立起引用关系,根据页面路径转换为驼峰的形式来作为页面名称,生成构建导航系统的路由配置。
运行时模块会提供一个入口包装的函数,将全局配置,转换后的路由配置,动态的构建入口根组件。
转换后的入口文件代码:
import { AppRegistry } from 'react-native';import { createReactNativeApp } from '@tarojs/runtime-rn'
import App from './src/app'import pagesIndexIndex from './src/pages/index/index';import pagesIndexAbout from './src/pages/index/about'
var config = {"appConfig":{"pages":["pages/index/index","pages/index/about"],"window":{"backgroundTextStyle":"light","navigationBarBackgroundColor":"#fff"}}}const routers = [ { name:'pagesIndexIndex', component: pagesIndexIndex },{ name:'pagesTabbarHome', component: pagesIndexAbout }]AppRegistry.registerComponent('app',createReactNativeApp(App,{config,routers})
运行时调用 createReactNativeApp 函数,在这个函数完成初始化,这个函数里主要做了些什么?
function createReactNativeApp (App,config){ return class Entry extends React.Component{ ... render(){ return React.createElement(TCNProvider, { ...this.props }, React.createElement(App, { ...props }, createRouter(config.routerConfig) )) } }}
实现对页面支持,其基本思路和入口一致的,在编译阶段,注入页面包装的函数,在运行时阶段,完成页面配置,页面函数等相关支持。
import { createPageConfig } from '@tarojs/runtime-rn'
//页面文件import pagesIndexIndex from './src/pages/index/index'//页面configimport pageConfig from './src/pages/index/index.config'
export default createPageConfig(pagesIndexIndex,pageConfig)
在 Taro 页面组件中,根据页面适配,需实现对以下两个内容的支持。
在 React Native 端,也保持和 Taro 的 React 组件写法是完全一致, 通过运行时函数 createPageConfig,实现对于面函数与生命周期函数的支持。
页面函数支持
对于微信的页面函数,根据页面config配置文件来控制是否触发, disableScroll 是否可滚动, enablePullDownRresh 是否开启下拉刷新。
生命周期支持
对于生命周期函数 componentDidShow, componentDidHide,这两个函数的触发条件:
实现上述函数,基本思路:
Current对象
在 Taro 3.0 之后,小程序端没有自定义组件,也不再有 this.scope 和 this.componentType,this.$router 的概念,对于需要获取页面切换的参数,当前页面的实例对象,通过提供了 getCurrentInstance 方法,返回 Taro 全局变量 Current ,包含路由,应用与页面实例,包含三个属性:
在 React Native 端,也是调用 getCurrentInstace 方法来返回 Current 对象
const pageRef = this.screenRef const inst: PageInstance = { config: pageConfig, route: pagePath, onShow () { const page = pageRef.current if (page != null && isFunction(page.componentDidShow)) { page.componentDidShow && page.componentDidShow() } }, ... }
有开发者提到,对于目前已经存在的 React Native 项目,在不修改原来的页面和导航的前提下,是否可以接入Taro?
答案是肯定的,基于 Taro 3 整体的设计方案,与现有业务的结合接入,我们也给出了对应的方案。
对于已有 React Native 项目接入 Taro,需要支持以下几点:
关于打包方案,Taro3 React Native 的打包方案是基于 Metro , 编译打包会生成支持Taro的 Metro 配置,并与业务配置合并得到最终的配置进行打包,能够很好的与现有业务进行融合。
关于路由统一处理,Taro React Native 的路由是基于页面的配置,封装的React Navigation的方案,与现有业务的路由结合,入口仍然按照原来的方式,Taro 页面路由可自行加入,完成路由的处理。
因此,我们提供了一种比较灵活的接入方案,其基本思路:支持导出 Taro 默认的 Metro 配置,与业务配置合并得到最终打包配置,提供支持Taro写法的运行时方法,处理页面编译配置,页面函数等相关内容
import { createPageConfig } from '@tarojs/runtime-rn'import PagesTaroPageApi from './src/pages/taroPage/api'import PagesTaroPageApiConfig from './src/pages/taroPage/api.config'
<Stack.Screen name="PagesTaroPageApi" component={createPageConfig(PagesTaroPageApi, { pagePath: '/pages/taroPage/api',...PagesTaroPageApiConfig})} />
这种方案可以较为方便的和现有项目结合,但需注意两点:
总结
Taro3 React Native 是基于 Metro 打包,通过自定义 transformer 来适配 Taro 的样式和页面支持,提供运行时函数,可以方便的支持到 Taro 页面配置与相关函数 ,更加的方便灵活,也更加贴近React Native生态,也可更方便的与现有业务融合,在不跨端的项目中也可以使用,能够大大提升我们的开发效率。
当然,我们的方案也还还存在进一步优化的空间,比如支持组件与API运行时自定义扩展,在不同的业务中,有些组件和API存在差异性,如地图,跟业务有一定的关联性,可按需要接入百度或高德地图等。
完整实例:http://github.crmeb.net/u/defu
来自 “开源世界 ” ,链接:http://ym.baisou.ltd/post/599.html,如需转载,请注明出处,否则将追究法律责任。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。