Taro3.2 适配 React Native 之运行时架构详解

导读css

由 58 前端团队主导的 Taro 3 适配 React Native 工做已完成有一段时间了。目前发布了多个体验版,也将在3月底迎来正式版。基于 Taro 的良好架构演变,适配 React Native 的方案的也作了较大调整,本文将主要介绍 Taro 3 适配 React Native 运行时相关的详细设计与实现。html

 

背景前端

Taro 已经进入3.0 时代,相对于 Taro 1/2 来讲,采用重运行时架构,可让开发者可以得到完整的 React/Vue 等框架开发体验,所以,咱们在设计 Taro3 React native 的方案时,也是基于运行时方案,增长 taro-runtime-rn 包来适配 React Native 端,使得 Taro 标准的 React 代码可运行在 React Native 端,让开发者能够低成本的扩展到 React Native 端。react

 

方案设计git

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
github

在设计 Taro3 React Native 方案之初,咱们但愿能够与小程序端标准较为一致的方案,对比了两种方案:小程序

  • 支持 React, Vue 开发,与小程序的设计思路一致,让 React Native 去模拟浏览器的 BOM/DOM API ,实现 React Native 的 renderreact-native

  • 支持 React 开发,经过编译和运行时去适配 Taro 的写法api

以上两种方案,若是采用基于小程序的方案,会存在如下问题:浏览器

  1. 基于小程序的方案,运行在 React Native 端,性能会有所降低,且方案更加复杂化,维护成本过高

  2. 脱离 React Native 生态,好比一些本来可直接使用的组件,须要作一层适配才可以使用

所以,咱们采用第二种方案,更好的贴近 React Native 生态,经过编译和运行时适配,让 Taro3 的 React 代码能够方便的扩展到 React Native 端。

 

详细设计

Taro3 React Native 总体方案的设计思路:基于 Taro 源码,利用 Metro 打包直接生成 jsBundle,经过编译和运行时适配到 Taro 的写法。

https://mp.weixin.qq.com/s/-7G7NMHX8ol99QxkswFOxg

直接基于源码去打包运行时适配,如何作适配,须要适配哪些内容?

  1. 入口文件及配置,Taro 入口写法是基于小程序的方案,需将其转换为 React Native 的入口及路由导航系统

  2. 页面的配置,对下拉属性,滚动,页面 Title 等相关设置

  3. 生命周期, componentDidShow, componentDidHide 的支持

  4. 页面函数,onPullDownRefresh, onPageScroll 等

  5. 事件、Taro 自定义 Hooks、 Current 对象

  6. 样式的支持,这里不过多的赘述,后续会有详细的文章说明

方案实现

Taro3 React Native 是总体方案是利用 Metro 基于 Taro 源码打包。

Metro 是针对 React Native 的 JavaScript 模块打包工具,接收一个入口文件和打包配置,将项目中全部依赖打包在一个或多个js文件。

打包过程会分为三个阶段:

  • Resolution:构建模块的依赖图,处理模块的依赖关系

  • Transformation:负责将模块转换成目标平台能够理解的格式

  • Serialization:模块转换序列化,组合这些模块来生成一个或多个包

对于 Taro 写法的支持,咱们在 Transformation 转化阶段,经过自定义的 taro-rn-transformer 与 taro-rn-style-transformer 对 Taro 的代码进行转换。

  • 在编译阶段,页面源文件都会进入到自定义的 taro-rn-transformer ,在 rn-transformer 中,会根据编译配置,一是,入口及页面会
    注入运行时处理函数, 二是,在 React Native 中,样式并无全局概念,对于 Taro 中定义的全局样式,好比 app.scss 等,在进入到 rn- transformer,会全局样式引入到页面中,支持到全局样式。

  • 在代码运行阶段,运行时处理函数会适配到Taro的相关内容,包含动态构建导航,页面配置,生命周期函数等相关内容,完成对入口,导航与页面的支持。

Taro 3 React Native中,运行时方案主要包含三个模块 ,各个模块之间的关系:

  • taro-router-rn,基 React Navigation 对路由进行封装,提供动态建立导航的方法给运行时,而且封装导航相应的API内容

  • taro-runtime-rn,运行时的核心内容,主要提供两个包装方法,一是 createReactNativeApp,用来对页面入口的相关处理,二是 createPageConfig,页面的包装方法,完成对页面的适配,包含生命周期,页面的配置等。

  •  taro-rn-transformer, 编译时注入页面的包装方法和入口方法,并将入口的全局样式注入到页面中。

对于 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 函数,在这个函数完成初始化,这个函数里主要作了些什么?

  1. 根节点Provider的注入

  2. 导航初始化

function createReactNativeApp (App,config){ return class Entry extends React.Component{ ... render(){ return React.createElement(TCNProvider, { ...this.props }, React.createElement(App, { ...props }, createRouter(config.routerConfig) )) } }}
  • 对于根节点Provider注入,因为在 taro-component-rn 的 Picker 组件是封装的 Ant-Design 组件,须要注入 Ant-Design 的 Provider

  • 对于导航系统初始化,Taro 3 仍然是采用 React Navigation,和 Taro 1/2 的差异是,升级到了 5.x 的版本。封装导航模块,根据转换生成的路由配置,提供 createRouter 的方法,动态去建立路由节点,构建出导航系统

页面支持

实现对页面支持,其基本思路和入口一致的,在编译阶段,注入页面包装的函数,在运行时阶段,完成页面配置,页面函数等相关支持。


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 页面组件中,根据页面适配,需实现对如下两个内容的支持。

  • 页面函数,包括了 onReactBottom, onPullDownRresh 等

  • 生命周期函数,包含了 componentDidShow, componentDidHide,这两个函数对应小程序的 onHide, onShow

在 React Native 端,也保持和 Taro 的 React 组件写法是彻底一致, 经过运行时函数 createPageConfig,实现对于面函数与生命周期函数的支持。

页面函数支持

对于微信的页面函数,根据页面config配置文件来控制是否触发,
disableScroll 是否可滚动, enablePullDownRresh 是否开启下拉刷新。

  1. 对于onPageScroll,onReachBottom , onPullDownRresh 都与页面滚动相关联, 当 config 配置 disablecroll 不为true时,对应的页面最外层会用 ScrollView 包含对应的页面组件,实现对页面函数的支持

  • onPageScroll, 经过监听 ScrollView 的 onScroll 方法实现

  • onReachBottom, 监听页面滚动动画结束函数 onMomentumScrollEnd ,来判断当前的离底部高度,最终来触发该函数

  • onPullDownRresh, 当enablePullDownRresh为 true 时,开启下拉刷新,经过封装 refreshControl 来实现

    function createPageConfig(Component,config){ const WrapScreen = (PageComponent) => { return class PageScreen extends React.Component{ ... render(){ return <ScrollView ... onScroll={()=>{}} refreshControl={<RreshControll />} > <PageComponent /> </ScrollView> } }  } return WrapScreen(Component)}
  1. onResize , onTabItemTap ,是基于 React Native 现有方案的实现

  • onResize, 在 React Native中,可监听屏幕高度变化,在 Taro 中,是经过监听屏幕的宽高变化来触发该方法

  • onTabItemTap , TabBar 是和导航相关联,咱们导航是基于 React Navigation 的封装,监听导航的 tabPress 方法来触发 onTabItemTap

生命周期支持

对于生命周期函数 componentDidShow, componentDidHide,这两个函数的触发条件:

  1. 当页面发生跳转时

  2. 当App进行先后台切换的时

实现上述函数,基本思路:

  • App先后台切换时,经过监听 AppState 的状态变化,状态切换的变化,可判断是从前台到后台,从而来触发对应的函数

  • 咱们的路由导航系统是基于 React Navigation, 页面切换时,导航提供了页面聚焦和是失去焦点时触发 focus 与 blur 事件,经过监听这两个事件,判断当前页面是否可见来触发对应函数

     

Current对象

在 Taro 3.0 以后,小程序端没有自定义组件,也再也不有 this.scope 和 this.componentType,this.$router 的概念,对于须要获取页面切换的参数,当前页面的实例对象,经过提供了 getCurrentInstance 方法,返回 Taro 全局变量 Current ,包含路由,应用与页面实例,包含三个属性:

  • Current.app,返回当前小程序应用实例

  • Current.page,返回当前小程序页面实例,

  • Current.router,返回当前小程序路由信息

在 React Native 端,也是调用 getCurrentInstace 方法来返回 Current 对象

  • 对于 app 和 page ,返回小程序规范实例,可经过此实例调用小程序规范生命周期。其实现思路是,当页面切换时建立一个对象,对象包含小程序的生命周期方法,当调用该方法时,经过 ref 关联到的当前页面,来 call 当前页面的方法。

 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() }  }, ...  }
  • 对于 router,基于 React Navigation 导航 获取到路由参数,返回到 router 对象中,目前暂不支持 onReady 等生命周期方法

     

原生 React Native 应用支持

有开发者提到,对于目前已经存在的 React Native 项目,在不修改原来的页面和导航的前提下,是否能够接入Taro?

答案是确定的,基于 Taro 3 总体的设计方案,与现有业务的结合接入,咱们也给出了对应的方案。

对于已有 React Native 项目接入 Taro,须要支持如下几点:

  1. 与原有页面一块儿打包方案结合

  2. 路由须要统一处理

  3. 支持 Taro 的编译配置,页面函数等

关于打包方案,Taro3 React Native 的打包方案是基于 Metro , 编译打包会生成支持Taro的 Metro 配置,并与业务配置合并获得最终的配置进行打包,可以很好的与现有业务进行融合。

关于路由统一处理,Taro React Native 的路由是基于页面的配置,封装的React Navigation的方案,与现有业务的路由结合,入口仍然按照原来的方式,Taro 页面路由可自行加入,完成路由的处理。

所以,咱们提供了一种比较灵活的接入方案,其基本思路:支持导出 Taro 默认的 Metro 配置,与业务配置合并获得最终打包配置,提供支持Taro写法的运行时方法,处理页面编译配置,页面函数等相关内容

  • 提供 tarojs/rn-supporter 的包, 导出Taro3 的 Metro 配置,可支持如下内容

    • 支持Taro样式写法

    • 支持Taro编译配置

    • 支持Taro运行时配置

    • 支持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})} />

这种方案能够较为方便的和现有项目结合,但需注意两点:

  1. 业务需自行处理导航

  2. 页面组件须要运行时函数包装

     

总结

Taro3 React Native 是基于 Metro 打包,经过自定义 transformer 来适配 Taro 的样式和页面支持,提供运行时函数,能够方便的支持到 Taro 页面配置与相关函数 ,更加的方便灵活,也更加贴近React Native生态,也可更方便的与现有业务融合,在不跨端的项目中也可使用,可以大大提高咱们的开发效率。

固然,咱们的方案也还还存在进一步优化的空间,好比支持组件与API运行时自定义扩展,在不一样的业务中,有些组件和API存在差别性,如地图,跟业务有必定的关联性,可按须要接入百度或高德地图等。

 

完整实例:http://github.crmeb.net/u/defu

来自 “开源世界 ” ,连接:http://ym.baisou.ltd/post/599.html,如需转载,请注明出处,不然将追究法律责任。

相关文章
相关标签/搜索