如果你应用 Taro 开发感觉 Bug 少,那阐明你的 React 代码写得很标准。 -- Taro团队

趁前段时间做基于taro2的微店铺以及taro2升taro3的我的项目教训,简略介绍Taro2跟Taro3的各自的优缺点以及理论应用场景下的语法区别,并分享Taro3降级中和应用中的踩坑点。

举荐浏览

小程序跨端框架开发的摸索与实际

Taro1/2

Taro3之前的整体架构能够看成两局部:编译时和运行时。这里解释一下两者的用处:

编译时:

通过对⽤户的 React 代码进⾏编译来转化代码语法,如jsx转小程序xml等,甚至转换成各个平台(抖音小程序、微信小程序、H5等等)都能够运⾏的代码。编译时工作流程次要是通过babel 将 Taro 代码解析成形象语法树,而后操作语法树生成指标平台的代码,也就是parse -> replace -> generate这样一个工作流程。

build:weapp编译微信小程序端为例:

render() {  return (      <View>      {        dataList.map((data, index) => (<Text key={index}>{data.title}</Text>))      }    </View>  )}

通过babel转换后:

<view wx:for="{{dataList}}" wx:for-item="data" wx:for-index="index">  <text>{data.title}</text></view>
咱们都晓得 JSX 是一个 JavaScript 的语法扩大,它的写法变幻无穷,非常灵便。这里咱们是采纳 穷举 的形式对 JSX 可能的写法进行了一一适配,这一部分工作量很大,实际上 Taro 有大量的 Commit 都是为了更欠缺的反对 JSX 的各种写法

这是摘自官网对taro2编译时的一句形容,因为应用穷举的适配形式,势必会造成jsx的各种各样的奇怪的bug产生和各种开发时的限度。

运行时

能够晓得的是,咱们开发taro我的项目时,援用的是taro库上面的api和组件,调用相似微信原生的api,如: wx.getSettings,在taro外面须要从 @taro/taro援用,而后调用 Taro.getSettings,组件则是通过@taro/components援用。这是因为Taro制订了一套运行时的规范组件库和api,通过对原生api进行拓展和配合编译时曾经抹平了状态、事件绑定、页面配置和生命周期等的差别,实现了框架的适配工作。具体点形容:

编译后的taro代码实现了 BaseComponentcreateComponentBaseComponent的作用次要是重写了react外面的render、setState等外围代码,createComponent 次要作用是调用 Component() 构建页面,解决以下等对接工作实现适配:

  • 将组件的 state 对应为小程序组件配置对象的 data
  • 将组件的生命周期对应为小程序组件的生命周期
  • 将组件的事件处理函数对应为小程序的事件处理函数
  • ...

简略来说就是先将代码编译成各个平台结构化语言的代码,而后通过适配器模式等等办法适配到各个平台可能让之运行起来,整个Taro2的架构编译时做的工作占次要局部,运行时工作量较小。

总结

  • 重编译时,轻运行时:这从两边代码行数的比照就可见一斑。
  • 编译后代码与 React 无关:Taro 只是在开发时遵循了 React 的语法。
  • 间接应用 Babel 进行编译:这也导致在工程化和插件方面的羸弱。

Taro3

Taro3能够大抵了解为解释型架构,这个工作就次要是在运行时"对代码进行解释",怎么了解呢?降级为Taro后你能够发现package.json文件外面多了个(当然不止这一个)@taro/runtime的依赖,关上包所在目录:

惊奇的发现了咱们在web中才会有的bomdom相干的关键字,原来Taro3本人实现了一套相似浏览器的BOM/DOM那一套API,通过webpack的plugin注入到小程序的逻辑层,打包编译后,你最终的代码都是基于BOM/DOM这几个API来实现你的具体性能,比方:不论什么平台,都有本人一套元素dom的规定,都有各自平台的相似bom的全局api规定,Taro3做的就是整合这些厂家的规定封装为相似BOM/DOM的思维去用,也就是说,我不论你开发时用的什么框架,我只有保障你运行时能帮你适配到各个平台即可。

这样做的最直观的益处就是后面说到的,不再受限制与框架自身了,实践上来说,Taro不仅能够反对Vue和React,也能用jquery、Angular等等的库进行跨端开发。站在React的使用者角度:通过taro2和taro3两个我的项目开发教训来说还有一点最直观的感触,taro3中写JSX更加难受了!其实就更加敌对的反对JSX这一点,应该是牵强附会的,因为taro的架构其实就是有限靠近于React的开发体验,适配的工作是通过运行时的BOM/DOM去实现的,而不是像之前版本一样,通过穷举 的形式对 JSX 的写法进行适配。

有个关键点:既然Taro3本人实现了BOM/DOM这一套api,而react的中的渲染器,如react-dom中调用的是浏览器的BOM/DOM的api,那taro必定会有本人一套渲染器来链接react的协调器(reconciler,diff算法所在阶段)和taro-runtime 的 BOM/DOM api。源码门路:@tarojs/react,description外面的形容如下:"like react-dom, but for mini apps."

如何抉择

从上述Taro 3架构来说,扭转了Taro1/2的重编译时的场面从而转向重运行时,其实重编译时的局限性不仅是对开发者的开发标准有更大的限度,也不利于保护taro框架的源码,因为一旦各厂商增删改了她们小程序的某个标准或者api时,要去保护降级taro的代码老本是比拟高的。

但这不意味着taro3肯定是最适宜的版本,同等条件下,编译时做的工作越多,也就意味着运行时做的工作越少,性能会更好。随时时代的进度,硬件的条件越来越好,就义硬件的前提下可能带来更好的开发体验兴许是比拟理智的抉择,然而在逻辑比较复杂或者须要更多运行内存的小程序,还是要侧重于重编译时的Taro2!

迁徙指南

降级Taro Next前,请仔细阅读官网迁徙指南

转换内部款式 withExternalStyle

Taro1/2 中需指定addGlobalClass能力应用内部的 className,然而 Taro3 晋升了所有的款式文件到 common.css 中,所以须要对外部款式进行转换。

转换 hooks withHooks

Taro1/2 中所有的 hooks 和一些非凡的性能组件如 Component, memo 等从 Taro 中导出。但 Taro3 中只保护了 Taro 独有的 hooks 和性能组件,所以将这些从 Taro2 中拆分进去。

转换类组件生命周期

React 中有一些废除的生命周期须要加上 UNSAFE 的前缀标识。

降级 mobx4 到 mobx6

如我的项目应用了mobx,请留神此条规定。

装璜器目前计划不稳固,在 mobx6 中更是缩小了装璜器的应用。而且 Taro3 中应用 mobx-react 而不是由 Taro 保护的 mobx-taro。

转换页面配置

页面配置文件独自拆分进去。

转换路由援用

拜访路由和路由参数应用 Taro3 的新的 api。

转换作用域援用 withScope

Taro1/2 应用编译时的框架,Taro3 应用的是运行时的框架,所以 scope 间接废除了。在转换中尝试进行兼容解决。

转换 hidden 属性

Taro1/2 中废除了自定义的一个属性 hidden。

将全局款式的 css 批改为 module.less

Taro3 中为了缩小组件款式文件的痛点,将所有的独自作用域的款式文件全副都晋升到了全局。间接转换后如不应用 css module,则会产生大量的款式凌乱干预。所以为了放弃款式的一致性,须要把款式进行转换,将 css 转换为 css module。

package.json所依赖的 taro 极其相干的依赖手动更改(截止 2022-01-25,Taro 最新版 3.4.0)

{  "@tarojs/cli": "3.3.17",  "@tarojs/components": "3.3.17",  "@tarojs/react": "3.3.17",  "@tarojs/runtime": "3.3.17",  "@tarojs/taro": "3.3.17",}

删除掉 taro1/2 中的 config/index.js 中的对于 babel 的配置,新增文件babel.config.js

module.exports = {  presets: [    [      'taro',      {        framework: 'react',        ts: true,      },    ],  ],};

配置 config/index.js 中,指定应用的框架为 react

{  framework: 'react',}

批改我的项目入口文件 app.ts

// 批改render函数,默认render this.props.childrenrender() {    return this.props.children}// 批改导出,间接将App导出export default App;// Taro.render(<App />, document.getElementById('app'));

原生组件的应用

“Taro3 的组件是没有配置文件的,因而 usingComponents 必须配置在“页面”的配置文件中”。然而 Taro1/2 的组件容许申明配置文件,所以在 Taro3 中须要把这些原生组件的配置申明晋升到具体援用具体的页面。可参考 #withUsingComponentForHaicaoyun

组件标签废除

在 Taro1/2 中,对应的组件将会编译成同名的组件。例如 HcyButton 组件编译成 hcy-button,所以如果我的项目中解决款式文件时对对应的款式做了定义,那么在 Taro3 中须要手动进行批改。

page.$component的应用

在 Taro1/2 中容许通过$component 的形式拜访具体组件的 Taro 实例,进而能够拜访类组件的任意办法和属性,甚至批改其状态。例如上面示例,拜访上个页面的实例,并设置其状态

const pages = getCurrentPages();const prevPage = pages[pages.length - 2];const prevPageInstance = prevPage.$component;prevPageInstance.setState({  formItems: [],});

这种在 Taro3 中并没有简略的替换计划,只能对所有应用这部分的中央进行一种适合的重构

taro2升taro3中可能遇到的报错

  1. Error: module "app.js" is not defined;module "common.js" is not defined (分包后公共组件打包问题)
  2. webpack Parser.pp$4.raise 报错 (webpack环境编辑应用的DOMAIN='',应该应用DOMAIN='""')
  3. Can't resolve './style/index.scss' (taro ui2.+和taro3.+版本不兼容)
  4. TypeError: Cannot read property 'prototype' of undefined (留神第三方库的依赖援用导致不兼容问题)
  5. MobX,Store Is Not Available, Make Sure It Is Provided By Some Provider (入口文件app.js应用<Provider {...store}></Provider>)注入全局store
  6. TypeError: Cannot read property 'getBehaviorPageData' of undefined (https://github.com/NervJS/tar...)(Taro.getCurrentPages获取到的page实例上没有$component)
  7. taro门路别名须要在webpack也配置一下
  8. 款式错乱问题,taro3中如不应用css module,则默认全局款式。

开发Taro2注意事项

凭记忆写下几点,具体问题可能与版本号也有关系,仅供参考

  1. 有状态组件尽量应用类组件开发,在页面中尤其留神这点,函数式组件中hooks和一些api性能bug较多,甚至某些钩子呈现不执行的bug
  2. 函数式组件中无奈通过Taro.createSelectorQuery获取元素信息,因为taro2须要传递scope,而在函数组件中无奈获取,及时应用useScope
  3. useEffectuseEffectLayout执行机会与一般的react我的项目有区别
  4. useDidShow在抖音端不执行
  5. Taro.hideLoading 微信真机上会同时敞开 Taro.showToast
  6. 组件中不能导出常量,如果须要常量,须要另开一个文件独自导出应用
  7. jsx中渲染函数前缀必须是render结尾,如renderItem = () => xxx
  8. Jsx中应用switch语法不能用default分支,会提醒你逻辑多余
  9. jsx中试用if..else..语法渲染节点,有时候并不会如你所意,尝试定义一个变量node,在分支中赋值给node后,最终return这个node节点解决问题
  10. 如果应用了mobx,抖音端在jsx条件渲染的时候须要用toJS包一下,如: toJS(list).length > 0 && <View>xxx</View>
  11. Swiper组件的nextMarginpreviousMargin属性在h5端有效
  12. h5有时候容器高度会比小程序出现的高,需设置box-sizing: border-box;
  13. 不应用 ViewText 标签选择器,H5内不失效。解决方案:应用className

开发Taro3注意事项

凭记忆写下几点,具体问题可能与版本号也有关系,仅供参考

  1. 更新组件会把兄弟节点也从新渲染了,问题形容

    解决方案:给兄弟节点或本身减少层级

  2. 应用Taro.createAnimationanimationData的初始值不能为null,否则适度动画的成果会生效,间接出现最终的款式,解决这一问题能够应用{}
  3. 版本3.4.0之前Taro.createAnimation在H5端生效
  4. 因为层级过深导致的渲染问题能够尝试应用CustomWrapper组件包裹,它的作用是创立一个原生自定义组件。对后辈节点的 setData 将由此自定义组件进行调用,达到部分更新的成果,从而晋升更新性能。
  5. video组件有时在第一次渲染的时候获取不到时长,无奈自动播放问题。
  6. 伪元素和伪类在小程序端生效
  7. 全局款式问题,taro3中如果不是css module,则默认款式文件作用所有组件
  8. Image在h5的实现是有一层包装层,这点在taro2中同样存在
  9. ScrollView中应用position: sticky有效,这点在taro2中同样存在
  10. hidden异样,通过 state 数据管制显隐,不会失效。解决方案:通过三目运算解决