首页
学习
活动
专区
工具
TVP
发布

Taro

作者头像
ayqy贾杰
发布2019-06-12 15:03:31
1.6K0
发布2019-06-12 15:03:31
举报
文章被收录于专栏:黯羽轻扬黯羽轻扬

一.目标定位

一套遵循 React 语法规范的多端统一开发框架

一种多端代码转换方案,这里的“端”是指微信小程序、Web、ReactNative、百度小程序、支付宝小程序、头条小程序、快应用等等

具体地,把一份类React源码,通过“编译”转换成兼容目标端的形式,即:

             转换
nerv业务代码 ------> xx小程序业务代码 +
                   Web业务代码 +
                   ReactNative业务代码

目的是降低开发成本,提高效率:

让原本只能运行在一端的项目获得多端运行的能力,降低开发者的重构成本。

二.思路探索

初衷

用React写微信小程序。

微信小程序原生方式开发起来太费劲,遂想用React开发微信小程序

延伸

在React业务代码转微信小程序代码这个最初的需求实现之后,发现依靠同样的转换思路可以适配多端,即从1对1延伸到1对n

P.S.其中Nerv是一种类React框架,API与React类似

P.S.Taro组件库之所以以微信小程序为标准,也是初衷使然(都做完了不能浪费啊)

思路

想要一份代码通吃n端,无非2种思路:

  • 直接从1端向n - 1端转换
  • 加一层抽象,从这层抽象转换到n

以Bash与Batch(Windows批处理脚本)为例,如果只写一份脚本,想既能在*nix跑,又能在Windows跑,第一种思路只需要实现1个东西(从bashn - 1端转换):

function bash2batch(bash) {
 // ...
 return equivalentBatch;
}

或者(从batchn - 1端转换):

function batch2bash(batch) {
 // ...
 return equivalentBash;
}

如果能实现AtoB,一份A就可以适配AB了,但“硬”转通常比较困难,因此在Bash与Batch的场景,诞生了第二种思路的实现:

Batsh: A language that compiles to Bash and Windows Batch.

也就是加一层抽象C,再分别实现CtoACtoB,从Batsh这层抽象转换到n端:

// 1.定义抽象层Batsh
const batsh = 'Neither bash nor batch';
// 2.实现抽象层向2端转换
function batsh2batch(batsh) {
 // ...
 return equivalentBatch;
}
function batsh2bash(batsh) {
 // ...
 return equivalentBash;
}

类似地,Taro也采用了第二种思路,这层抽象就是Taro业务代码:

P.S.Taro业务代码即图中的Nerv代码,叫Taro代码更准确一些,因为增加了一些Taro特有的API支持(如Taro.getEnv()),是Nerv的超集

三.核心实现

以微信小程序为例,它由4部分组成:

  • 配置(JSON)
  • 模板(WXML)
  • 样式(WXSS)
  • 逻辑(JS)

配置与样式没什么好说的,难点在于模板的转换和逻辑的转换

P.S.ReactNative样式转换另说,也是一个难题,因为RN在选择器、属性名/值及默认值,甚至CSS特性支持程度都存在较大差异

编译转换

要把一份代码A转换成另一份代码B,需要做3件事情:

  1. 解析代码A生成抽象描述(AST)
  2. 根据一些映射规则操作AST,生成新的AST
  3. 根据新的AST生成代码B

P.S.关于编译转换的更多信息,请查看再看编译原理与Babel快速指南

模板的转换

把 JSX 语法转换成可以在小程序运行的字符串模板。

输入JSX:

render() {
 const { percent } = this.state; return (
   <View className='index'>
     <Button className='add_btn' onClick={this.props.add}>+</Button>
     { percent && <MyProgress percent={percent} strokeWidth={6} color='#FF4949' /> }
   </View>
 );
}

经@tarojs/transformer-wx转换,输出微信小程序模板

<block>
 <view class="index">
   <button class="add_btn" bindtap="funPrivatesBrJC">+</button>
   <block wx:if="{{percent}}">
     <my-progress percent="{{percent}}" strokeWidth="{{6}}" color="#FF4949"></my-progress>
   </block>
 </view>
</block>

ViewButton等都是Taro内置组件

Taro 以 微信小程序组件库 为标准,结合 jsx 语法规范,定制了一套自己的组件库规范

相关package如下:

  • @tarojs/components:支持Web环境Nerv组件库,通过编译替换为目标平台的原生标签/组件
  • @tarojs/taro-components-rn:支持ReactNative环境的React组件库(之所以ReactNative组件库独立出来,可能是因为差异较大,难以通过编译手段实现转换)

都会被转换成目标端的原生组件:

在小程序端,我们可以使用所有的小程序原生组件,而在其他端,我们提供了对应的组件库实现

但自定义组件my-progress在微信小程序中是不存在的,所以并不能如预期地跑起来

势必需要一种跨端组件定义,为此Taro提供了2个东西:

  • 跨端组件库Taro UI
  • 支持把自定义组件打包成各目标端支持的形式(具体见基于 Taro 开发第三方多端 UI 库)

前者解决有没有的问题,应对一般应用场景。后者开放一种自定义的能力,满足需要定制的场景

逻辑的转换

类似于组件库需要做多端适配,各端能力差异也同样需要适配:

组件库以及端能力都是依靠不同的端做不同实现来抹平差异

运行时框架负责适配各端能力,以支持跑在上面的Taro业务代码,主要有3个作用:

  • 适配组件化方案、配置选项等基础API
  • 适配平台能力相关的API(如网络请求、支付、拍照等)
  • 提供一些应用级的特性,如事件总线(Taro.EventsTaro.eventCenter)、运行环境相关的API(Taro.getEnv()Taro.ENV_TYPE)、UI适配方案(Taro.initPxTransform())等

实现上,@tarojs/taro是API适配的统一入口,编译时分平台替换

  • @tarojs/taro:只是一层空壳,提供API签名

平台适配相关的package有6个:

  • @tarojs/taro-alipay:适配支付宝小程序
  • @tarojs/taro-h5:适配Web
  • @tarojs/taro-rn:适配ReactNative
  • @tarojs/taro-swan:适配百度小程序
  • @tarojs/taro-tt:适配头条小程序
  • @tarojs/taro-qapp:适配快应用

P.S.与组件库适配方案不同的是,API干脆放弃编译转换这条路,直接整个替掉

实际上,要想只维护一份业务代码,那么Taro提供的API必定是n端API的并集,例如:

// 各小程序都支持的API
Taro.setStorage()
// 百度小程序专有API
Taro.textToAudio()
// 支付宝小程序与微信小程序参数处理上存在差异的API
Taro.getStorageSync()
// ...

这些API都可以直接使用,不用关心当前平台是否支持,因为运行时框架的适配工作的一部分就是抹平平台能力API差异,例如:

H5 端就无法调用扫码、蓝牙等端能力 采用微信小程序标准,所以这些 API 在 H5 端运行的时候将什么也不做。

同时在业务层区分目标环境,保证这些平台相关的代码仅在预期的目标环境下执行:

  • 编译时:process.env.TARO_ENV
  • 运行时:Taro.getEnv()

例如:

// 分平台调用API
if (process.env.TARO_ENV === 'swan') {
 Taro.textToAudio()
}
// 分平台使用不同组件
<View>
 {process.env.TARO_ENV === 'weapp' && <ScrollViewWeapp />}
 {process.env.TARO_ENV === 'h5' && <ScrollViewH5 />}
</View>

P.S.编译时静态的环境区分足够应对大多数场景了,运行时的环境区分仅备不时之需

四.结构

从设计上看,Taro方案分为3层:

业务层(类React代码)
---------------------
转换层(JSX转微信小程序)
---------------------
适配层 组件库(适配n端原生组件)
     运行时框架(适配n端API能力)
---------------------

此外,还有

  • 生态:UI库、路由、数据流管理、CSS预处理等
  • 构建:Web走Webpack,ReactNative走Expo的xdl,其余的各自走自己的IDE
  • Lint:对于转换层不支持的写法,通过静态检查给出一部分警告

五.源码简析

对应到具体实现,各部分对应的package如下(taro/packages/):

// 转换
babel-plugin-transform-jsx-to-stylesheet
taro-plugin-babel
taro-plugin-csso
taro-plugin-uglifyjs
taro-transformer-wx// 适配-组件库
taro-components-rn
taro-components// 适配-运行时框架
taro-alipay
taro-h5
taro-qapp
taro-rn
taro-swan
taro-tt
taro-weapp
taro// 生态
postcss-plugin-constparse
postcss-pxtransform
postcss-unit-transform
taro-async-await
taro-mobx-common
taro-mobx-h5
taro-mobx-prop-types
taro-mobx-rn
taro-mobx
taro-plugin-less
taro-plugin-sass
taro-plugin-stylus
taro-plugin-typescript
taro-redux-h5
taro-redux-rn
taro-redux
taro-router-rn
taro-router// 构建
taro-cli
taro-rn-runner
taro-webpack-runner// Lint
eslint-config-taro
eslint-plugin-taro// 其它(公共方法)
taro-utils

另外,还有个有意思的东西

// 微信小程序转Taro
taroize
// taroize 之后的运行时
taro-with-weapp

反向转换是另一扇门,就转换而言,从1对1延伸到1对n之后,下一个阶段就是n到1了,即:

// 目标端
A = weapp
B = ReactNative
C = ReactNative
// 抽象层
T = Taro// 第一阶段:1对1
T2A()
// 第二阶段:1对n
T2A(), T2B(), T2C()...
// 第三阶段:n到1
A2T(), B2T(), C2T()...

等到第三阶段完成,就天下大同了(随便拿个什么东西都能转换到n端)

P.S.目前(2018/12/9),A2T()(小程序代码转 Taro)已经待发布了,具体见版本计划

六.限制

限制方面感受最深的应该是JSX,毕竟JSX的灵活性令人发指(动态组件、高阶组件),同时微信小程序的模板语法又限制极多(即便通过WXS这个补丁增强了一部分能力),这就出现了一个不可调和的矛盾,因此:

JSX 的写法极其灵活多变,我们只能通过穷举的方式,将常用的、React 官方推荐的写法作为转换规则加以支持,而一些比较生僻的,或者是不那么推荐的写的写法则不做支持,转而以 eslint 插件的方式,提示用户进行修改

具体地,JSX限制如下:

  • 不支持动态组件
  • 不能在包含 JSX 元素的 map 循环中使用 if 表达式
  • 不能使用 Array#map 之外的方法操作 JSX 数组
  • 不能在 JSX 参数中使用匿名函数
  • 不允许在 JSX 参数(props)中传入 JSX 元素
  • 只支持class组件
  • 暂不支持在 render() 之外的方法定义 JSX
  • 不能在 JSX 参数中使用对象展开符
  • 不支持无状态组件(函数式组件)
  • props.children只能传递不能操作

对于这些转换限制,弥补性方案是Lint检查报错,并提供替代方案

除JSX外,还有2点比较大的限制:

  • CSS:受限于ReactNative的CSS支持程度(只能使用flex布局)
  • 标签:约定 不要使用 HTML 标签(都用多端适配过的内置组件,如ViewButton

P.S.囿于静态转换自身的限制,很多转换是没办法实现的

七.应用场景

当业务要求同时在不同的端都要求有所表现的时候,针对不同的端去编写多套代码的成本显然非常高

也就是说,当同一业务在多端有重叠需求时,Taro之类的多端代码转换方案才有意义

另一类场景是Taro最初想要解决的微信小程序开发体验问题,如果用Taro来开发微信小程序,一不小心还能适配多端,也是个不错的选择

参考资料

  • JDReact小程序双向转换工具介绍
  • 为何我们要用 React 来写小程序 – Taro 诞生记
  • 多端统一开发框架 Taro 1.0 正式发布
  • Taro 1.1 发布,全面支持微信/百度/支付宝 小程序
  • Taro深度开发实践
  • 多端统一开发框架 – Taro
  • 首个多端 UI 组件库 – Taro UI 发布
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-12-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 前端向后 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一.目标定位
  • 二.思路探索
    • 初衷
      • 延伸
        • 思路
        • 三.核心实现
          • 编译转换
            • 模板的转换
              • 逻辑的转换
              • 四.结构
              • 五.源码简析
              • 六.限制
              • 七.应用场景
                • 参考资料
                相关产品与服务
                云开发 CloudBase
                云开发(Tencent CloudBase,TCB)是腾讯云提供的云原生一体化开发环境和工具平台,为200万+企业和开发者提供高可用、自动弹性扩缩的后端云服务,可用于云端一体化开发多种端应用(小程序、公众号、Web 应用等),避免了应用开发过程中繁琐的服务器搭建及运维,开发者可以专注于业务逻辑的实现,开发门槛更低,效率更高。
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档