Taro 3 React Native 你想了解的都在这提案里

Easter79
• 阅读 1124

Taro 3 React Native 你想了解的都在这提案里

导读

58同城前端通道与京东凹凸实验室达成 Taro 项目开源合作,针对 Taro 3 React Native 支持方案,做了较大程度的重构。

经过较长时间的方案讨论,现已正式对外公布 RFC 提案,并征集社区意见。开源社区参与者可通过 https://github.com/NervJS/taro-rfcs/pull/8 或点击阅读原文参与提案讨论,期待您的参与。

提案详情

概述

在 Taro 中实现使用 React 开发 React Native。

动机

Taro 升级到 3 以后,React Native 无法使用,社区仍然有使用 Taro 开发 React Native 的需求。

使用案例

在页面组件和自定义组件开发中,与编写 React 项目遵循一致的规范。

入口组件生命周期、页面组件生命周期及页面事件处理函数,支持度与小程序相比会有差异,参考详细设计运行时部分。

API、组件支持度与小程序相比有些取舍,参考详细设计 API 及组件部分。

详细设计

本提案以 Taro 3 定义的标准为基础,完成 React Native 端的升级改造,同时在编译打包方案、API 及组件支持、React Native 项目接入灵活性等方面做了较大重构。

设计总结

  1. 将编译打包方案统一为 metro ,更贴合 React Native 生态体系。

  2. 相比 webpack + metro 的方式,可提升编译速度,整体方案也更为清晰。

  3. 相比 webpack 多 entry 模式,可降低包大小。同时解决多 entry 模式存在的一些问题,如 #7512。

  4. 提供更好的 sourcemap 支持,优化开发体验。

  5. 与 React Native APP 项目自身的 metro 配置,如分包等,可灵活合并。

  6. 运行时模块,按照 Taro 3 定义的标准进行改造。

  7. 与小程序及 H5 内部的写法保持基本一致,通过 metro transformer (类似 taro loader) 生成入口及页面代码,通过 taro-runtime-rn 包提供的方法包装入口组件及页面组件。

  8. 提升对页面事件处理函数的支持度。

  9. 增加对 Tab Bar 相关 API 的封装。

  10. 提升 API 及组件的支持度。

  11. 按社区调研结果优先级及难易程度进行支持,仍然以 expo 体系为主。

  12. API 及组件可按需集成,对于依赖原生的 API 和组件,提供完整的原生集成文档。

  13. 视工作量情况,逐步提升支持度,同时欢迎社区贡献。

  14. 提供更灵活的 React Native APP 接入方案。

  15. 不再锁定 React Native 版本,用户可在项目中自行安装 >=0.60 版本的 React Native,对于 >=0.57 && <=0.59 版本的支持将在后续推出。

  16. 除 React Navigation 外不强制依赖其他 Native Modules,用户可按需引入所需的原生依赖。相应原生依赖未安装时,相关接口不可用,但不会阻断程序运行。同时用户可根据自身应用情况,对所需 API 接口或组件进行替换。

  17. 不需要灵活定制的用户,仍可以使用我们提供的包含所需原生依赖壳工程项目,快速开始开发。

整体设计图示

Taro 3 React Native 你想了解的都在这提案里

大致流程如下:

  1. @taro/cli 中通过registerPlatform 注册 rn 平台;

  2. yarn dev:rn 获得编译配置,转为 babel.config.js 及 metro.config.js 配置;

  3. 所有 React Native 不支持的语法及配置,通过编译配置支持;

  4. 通过编译配置与 @tarojs/taro-rn-transformer 生成 React Native 的入口文件 index.ts;

  5. 入口文件引入 @tarojs/taro-runtime-rn 使用createReactNativeApp进行包装;

  6. 页面文件引入 @tarojs/taro-runtime-rn 使用createPageConfig 进行包装;

  7. 启动 metro bunlder;

  8. 在 React Native Shell 工程中运行 react-native run-ios 或 react-native run-android 加载 index.bundle。

本次新增及修改的包列表

包名 (packages下文件夹名)

包功能

*taro-cli

1. 获取公用编译配置及 RN 特殊配置,启动 rn-runner。
2. 检查依赖包是否正常安装:包括(react-native、component-rn、runtime-rn等)

*taro-component-rn

1. 增加对虚拟列表的支持。
2. 增加对旧版本不支持的组件的支持,详见组件改造小节。

*taro-rn

1. 增加对旧版本不支持的接口的支持,详见API改造小节。

taro-rn-runner

1. 与用户自定义的 metro 配置做合并。
2. 生成babel配置。
3. 引入style transformer、taro-rn-transformer 等包生成metro配置。
4. 启动bundler。
5. 支持通用编译配置,包括 defineConstants、copy、alias、sass、env 等。
6. 支持RN特殊配置,如样式缩放开关。
7. 引入 babel plugin  transform-jsx-to-stylesheet  以支持 className 写法。
8. 支持优先加载 .rn. 文件,Android加载 .android.文件,iOS加载 .ios.文件。
9. 编译平台包替换及 alias 配置。
10. 打包压缩、sourcemap 等能力支持。

taro-rn-transformer

1. 拦截入口文件 index.js/index.ts,根据配置,生成入口文件代码。
2. 根据 app.config 的 pages 配置,拦截页面文件,根据配置,生成页面文件代码。
3. 功能类似小程序及h5的 taro-loader。

taro-style-transformer

1. 支持 sass、less、stylus、postcss文件的引入,转化为 styleObject。
2. 支持 sass 的全局引入配置
3. 引入 postcss-pxtransform 以支持 React Native 单位转化。

taro-asset-transformer

1. 支持引入静态资源,转化图片等资源为 base64。

babel-plugin-transform-jsx-to-stylesheet

1. jsx 支持 className 属性,将各种语法转化为 styleObject[‘className’]等。

bable-preset-taro-rn

1. 将所需 babel plugin 进行合并,统一管理。

taro-runtime-rn

1. 提供 app 包装方法。
2. 提供 page 包装方法。
3. 支持 pulldownrefresh 等页面事件处理函数支持。
4. 支持页面配置,title、navigatebar等。
5. 提供 Taro3 新增的全局对象 Current 等。

taro-router-rn

1. 封装所有的路由处理。
2. 支持新增的TabBar相关API。
3. 支持tabbar相关配置。
4. 支持icon配置。

注:带*为做修改的包,不带为本次新增或基本重构的包。

编译打包方案改造

对于通用配置的支持情况

配置

是否支持

方案

sourceRoot

支持

-

outputRoot

支持

-

designWidth

支持

-

defineConstants

支持

使用 babel-plugin-transform-inline-environment-variables 加入到运行环境中。

alias

支持

使用 babel-plugin-module-resolver 支持。

env

支持

使用 babel-plugin-transform-inline-environment-variables 加入到运行环境中。
包含 process.env.TARO_ENV 值为 rn

copy

不支持

-

plugins

不支持

-

presets

不支持

-

terser

支持

-

csso

支持

transformer 引入 csso。

sass

支持

-

webpack loader 配置替代方案

通过 webpackChain 修改配置已不再支持,对样式编译配置的修改使用如下代替配置。

rn: { sass: { options: ..., // https://github.com/sass/node-sass#options additionalData: ..., // {String|Function} 注入到所有 sass 文件中 sourceMap: boolean, // 不做支持 }, less: {}, stylus: {}, postcss: {}, }

平台差异化文件引用支持

通过配置 babel plugin,支持编译 React Native 平台时,优先使用 *.rn.*,编译 Android 平台优先使用 *.android.*,编译 iOS 平台优先使用 *.ios.*。

参考 babel-plugin-react-native-platform-specific-extensions 实现。

编译平台包替换及 alias 配置

将 @tarojs/taro-components 替换为 @tarojs/taro-components-rn,将 @tarojs/taro 替换为 @tarojs/taro-rn。

安装 babel-plugin-module-resolver,配置 babel.config.js。

{

plugins: [ [ 'module-resolver', { alias: { '@tarojs/components': '@tarojs/components-rn', '@tarojs/taro': '@tarojs/taro-rn', }, }, ] }

用户 metro 配置自定义合并

taro-rn-runner 将所需的 metro 配置作为 defaultConfig,项目中的 metro.config.js 作为配置载入。

Metro.loadConfig({}, defaultConfig);

sourcemap 支持

基于 metro 本身的 sourcemap,可自行调整配置。

TypeScript 支持

metro-react-native-babel-transformer 使用的 metro-react-native-babel-preset 默认支持 TypeScript。

样式语法支持

统一使用 transformer 实现

className 语法支持

通过操作 visitor 的 JSXOpeningElement,ImportDeclaration,Program.exit,实现 className 的语法转换。

参考 babel-plugin-transform-jsx-stylesheet 包做一些改进。

支持包括如下场景:

  1. class string 与 多class string;

  2. 对象 { active: this.props.isActive };

  3. 数组 ['header1 header2', 'header3', { active: this.props.isActive }];

  4. 三元运算this.props.visible ? 'show' : 'hide';

  5. 自定义函数 getClassName();

  6. 多个样式文件,对象进行合并;

层叠样式表预编译语言支持(sass less stylus postcss)

通过配置 met ro 的 transformer.babelTransformerPath 以支持 css 预编译语言,将编译结果导出为 styleObject。

参考如下实现:

  1. react-native-sass-transformer

  2. react-native-typed-postcss-transformer

  3. react-native-less-transformer

  4. react-native-stylus-transformer

sass.resource 支持全局

在使用 metro 的 sass transformer 时,将  sass.resource 配置的全局 sass 文件注入到样式文件头部。

单位转化

同 2.x 通过 postcss-pxtransform 插件,在使用 metro 的 postcss transformer 时,引入该插件。该功能支持关闭。

样式文件中跨平台支持

如:

/* #ifdef %PLATFORM% */ 样式代码 /* #endif */

仍然通过  postcss-pxtransform 支持 。

全局的 app.css 支持

将 app.jsx 中对样式的引用,合并至页面的 styleObject 中。

media-query-processor 及 viewport-units 支持

参考 react-native-dynamic-style-processor 实现。

Taro3标准运行时支持

使用方法及实现方案,与小程序和h5保持一致。

使用 taro-rn-transformer/app.ts 生成入口文件

metro transformer 中判断是否为 index.js,是的话,使用 taro-rn-transformer/app 传入编译配置,进行转换。

transformer 伪代码如下:

`var upstreamTransformer = require("metro-react-native-babel-transformer");
const { transform } = require("@tarojs/taro-rn-transformer/app");
const { pages } = require('./config');

module.exports.transform = function({ src, filename, options }) {
if (filename === 'index.js') {
return upstreamTransformer.transform(transform({ src, filename, {pages, ...options}}));
}
return upstreamTransformer.transform({src, filename, options });
};
`

生成后入口文件的伪代码如下:

`import App from './src/App';
import {AppRegistry} from 'react-native';
import {createReactNativeApp} from '@tarojs/taro-runtime-rn';
import config from './src/App.config';

// 遍历配置生成的页面路由表
import PagesIndexIndexScreen from './src/pages/index/index';
import PagesIndexAboutScreen from './src/pages/index/about';
const routers = [{
name: 'PagesIndexIndex',
component: PagesIndexIndexScreen
}, {
name: 'PagesIndexAboutScreen',
component: PagesIndexAboutScreen
}];
const buildConfig = {};

AppRegistry.registerComponent('appName', () => createReactNativeApp(App, config, routers, buildConfig));
`

再经过 metro-react-native-babel-transformer 进行二次转化。

页面文件生成 taro-rn-transformer/page.ts

metro transformer 中判断是否为页面文件,是的话,使用 taro-rn-transformer/page 传入编译配置,进行转换。

生成后页面文件的伪代码如下:

`import {createPageConfig} from '@tarojs/taro-runtime-rn';
import config from './pagename.config';

// 原有的代码
import Componet from './pagename';

export default createPageConfig(Component, config);
`

再经过 metro-react-native-babel-transformer 进行二次转化。

createReactNativeApp

暴露给 @tarojs/taro-rn-transformer/app 调用,在 React Native 应用入口文件中调用,创建一个 React Native App 构造函数接受小程序应用规范对象。

createPageConfig

暴露给 @tarojs/taro-rn-transformer/page 调用,在 React Native 应用页面文件中调用,创建一个 React Native App 构造函数接受小程序页面规范对象。

Current

暴露给开发者的 Taro 全局变量,目前有三个属性:

  1. Current.app,返回当前小程序应用实例,非小程序端返回小程序规范应用实例,可通过此实例调用小程序规范生命周期。

  2. Current.page,返回当前小程序页面实例,非小程序端返回小程序规范页面实例,可通过此实例调用小程序规范生命周期。

  3. Current.router,返回当前小程序路由信息,非小程序端返回小程序规范路由信息。

针对 React Navigation 做一样的封装 。

页面事件处理函数支持

**函数
**

支持方案

onPullDownRefresh

基于 scrollView 的 refreshControll。

onReachBottom

监听 onMomentumScrollEnd。

onPageScroll

基于 scrollView。

onResize

基于 Dimensions。

onTabItemTap

基于 React Navigation tabPress 事件。

自定义 hooks 支持

useDidShow,useDidHide,usePullDownRefresh等,参考 H5 React 实现。

路由支持从外部直接带参数进入指定页面

基于 React Navigation Deep linking 进行实现。

API改造

API 改造

组件包 taro-rn fork 自 2.x 版本,增加对如下API的支持。

**API
**

实现方案

tabBar相关API

基于 React Navigation 进行封装。

VideoContext

基于 expo-av 进行封装。

CameraContext

基于 expo-camera 进行封装。

AudioContext

基于 expo-av 进行封装。

* Animation

基于 Animated 进行封装。

扫码

基于 expo-barcode-scanner 进行封装。

File相关API

基于 expo-file-system 进行封装

* Canvas相关API

基于 react-native-canvas 封装。

网络请求(downloadFile,uploadFile)

基于 fetch 及 expo-file-system 进行封装。

Resize 及 Keyboard 事件监听

Resize 基于 Dimensions,Keyboard基于

WebSocket

基于 React Native WebSocket Support 进行封装。

EventChannel,Events,eventCenter

基于 DeviceEventEmitter 进行封装。

RequestTask相关API

-

注:带*为评估优先级较低。

组件改造

组件包 taro-component-rn fork 自 2.x 版本,增加对如下组件的支持。

组件

实现方案

VirtualList

封装 FlatList。

MovableView 及 MovableArea

封装 Animated 及 PanResponder。

Label

封装 View 当点击时,会触发对应的控件更新。

PickerView 及 PickerViewColumn

基于 @ant-design/react-native 封装。

Navigator

封装对路由的操作。

Audio

基于 expo-av 进行封装。

Video

基于 expo-av 进行封装。

Camera

基于 expo-camera 进行封装。

*Canvas

基于 react-native-canvas 封装。

注:带*为评估优先级较低。

APP接入方式改造

appName 支持配置

因不同 APP 有不一样的 appName 配置,app.config.ts 增加配置项 appName,该参数用于注册入口组件。

import { appName } from './app.config'; AppRegistry.registerComponent(appName, () => App);

React Native 多版本支持

一期兼容 >= 0.60 版本,初始化会默认选择最新的稳定版本,用户根据需要可自行选择可兼容的版本进行安装。同时壳工程或自身APP需要使用相应的版本。

不强制依赖所有的 Native Modules

当 APP 中一些 Native Modules 不存在时,部分 API 不可用,此时弹出警告,而不是阻塞运行。

其他

状态管理支持

同 Taro 3 方案,由使用方自行处理。

SubPackages 分包

暂不支持分包,分包的配置的 subPackages 会被合并进入 pages。

壳工程

保留在原工程仓库 taro-native-shell 中,增加新的分支 0.63.0,0.62.0,0.61.0,0.60.0,均对应 Taro 3版本。

缺陷

0. 不支持使用 vue 或者 jQuery 框架编写 React Native。

1. 整体设计与小程序和 H5 有些差异。

2. 采用了 metro 编译打包方案,与 webpack 的方案有较大不同,需及时同步新版本迭代的更新,以支持一些新写法。

适配策略

0. 使用 Taro React 开发的代码,对不支持的组件及接口做兼容,安装有相应依赖后,即可运行于 React Native 环境。

1. 含有部分 React Native 的原生依赖,需要增加到各自的原生仓库里,或使用壳项目进行。

2. React Native 实现的样式是 css 的子集,无法做到完全兼容,需要由业务自行兼容,做跨端项目建议 React Native first。

作者简介:

陈志庆:58同城 前端架构师,技术委员会委员。

推荐阅读:

Taro 3 React Native 你想了解的都在这提案里

Taro 3 React Native 你想了解的都在这提案里

Taro 3 React Native 你想了解的都在这提案里

本文分享自微信公众号 - 凹凸实验室(AOTULabs)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Easter79 Easter79
2年前
swap空间的增减方法
(1)增大swap空间去激活swap交换区:swapoff v /dev/vg00/lvswap扩展交换lv:lvextend L 10G /dev/vg00/lvswap重新生成swap交换区:mkswap /dev/vg00/lvswap激活新生成的交换区:swapon v /dev/vg00/lvswap
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Wesley13 Wesley13
2年前
Java获得今日零时零分零秒的时间(Date型)
publicDatezeroTime()throwsParseException{    DatetimenewDate();    SimpleDateFormatsimpnewSimpleDateFormat("yyyyMMdd00:00:00");    SimpleDateFormatsimp2newS
Wesley13 Wesley13
2年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
2年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
2年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
3个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这
Easter79
Easter79
Lv1
今生可爱与温柔,每一样都不能少。
文章
2.8k
粉丝
5
获赞
1.2k