共计 6109 个字符,预计需要花费 16 分钟才能阅读完成。
如果你应用 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 代码实现了 BaseComponent
和 createComponent
,BaseComponent
的作用次要是重写了 react 外面的 render、setState 等外围代码,createComponent
次要作用是调用 Component()
构建页面,解决以下等对接工作实现适配:
- 将组件的 state 对应为小程序组件配置对象的 data
- 将组件的生命周期对应为小程序组件的生命周期
- 将组件的事件处理函数对应为小程序的事件处理函数
- …
简略来说就是先将代码编译成各个平台结构化语言的代码,而后通过适配器模式等等办法适配到各个平台可能让之运行起来,整个 Taro2 的架构编译时做的工作占次要局部,运行时工作量较小。
总结
- 重编译时,轻运行时:这从两边代码行数的比照就可见一斑。
- 编译后代码与 React 无关:Taro 只是在开发时遵循了 React 的语法。
- 间接应用 Babel 进行编译:这也导致在工程化和插件方面的羸弱。
Taro3
Taro3 能够大抵了解为解释型架构,这个工作就次要是在 运行时 “ 对代码进行解释 ”,怎么了解呢?降级为 Taro 后你能够发现package.json
文件外面多了个(当然不止这一个)@taro/runtime
的依赖,关上包所在目录:
惊奇的发现了咱们在 web 中才会有的 bom
跟dom
相干的关键字,原来 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.children
render() {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 中可能遇到的报错
- Error: module “app.js” is not defined;module “common.js” is not defined(分包后公共组件打包问题)
- webpack Parser.pp$4.raise 报错(webpack 环境编辑应用的 DOMAIN=”,应该应用 DOMAIN='””‘)
- Can’t resolve ‘./style/index.scss’(taro ui2.+ 和 taro3.+ 版本不兼容)
- TypeError: Cannot read property ‘prototype’ of undefined(留神第三方库的依赖援用导致不兼容问题)
- MobX,Store Is Not Available, Make Sure It Is Provided By Some Provider(入口文件 app.js 应用 <Provider {…store}></Provider>)注入全局 store
- TypeError: Cannot read property ‘getBehaviorPageData’ of undefined(https://github.com/NervJS/tar…)(Taro.getCurrentPages 获取到的 page 实例上没有 $component)
- taro 门路别名须要在 webpack 也配置一下
- 款式错乱问题,taro3 中如不应用 css module,则默认全局款式。
开发 Taro2 注意事项
凭记忆写下几点,具体问题可能与版本号也有关系,仅供参考
- 有状态组件尽量应用类组件开发,在页面中尤其留神这点,函数式组件中 hooks 和一些 api 性能 bug 较多,甚至某些钩子呈现不执行的 bug
- 函数式组件中无奈通过
Taro.createSelectorQuery
获取元素信息,因为 taro2 须要传递 scope,而在函数组件中无奈获取,及时应用useScope
useEffect
和useEffectLayout
执行机会与一般的 react 我的项目有区别useDidShow
在抖音端不执行Taro.hideLoading
微信真机上会同时敞开Taro.showToast
- 组件中不能导出常量,如果须要常量,须要另开一个文件独自导出应用
- jsx 中渲染函数前缀必须是 render 结尾,如
renderItem = () => xxx
- Jsx 中应用 switch 语法不能用 default 分支,会提醒你逻辑多余
- jsx 中试用 if..else.. 语法渲染节点,有时候并不会如你所意,尝试定义一个变量 node,在分支中赋值给 node 后,最终 return 这个 node 节点解决问题
- 如果应用了 mobx,抖音端在 jsx 条件渲染的时候须要用
toJS
包一下,如:toJS(list).length > 0 && <View>xxx</View>
Swiper
组件的nextMargin
和previousMargin
属性在 h5 端有效- h5 有时候容器高度会比小程序出现的高,需设置
box-sizing: border-box;
- 不应用
View
、Text
标签选择器,H5 内不失效。解决方案:应用 className
开发 Taro3 注意事项
凭记忆写下几点,具体问题可能与版本号也有关系,仅供参考
-
更新组件会把兄弟节点也从新渲染了,问题形容
解决方案:给兄弟节点或本身减少层级
- 应用
Taro.createAnimation
,animationData
的初始值不能为null
,否则适度动画的成果会生效,间接出现最终的款式,解决这一问题能够应用{}
- 版本 3.4.0 之前
Taro.createAnimation
在 H5 端生效 - 因为层级过深导致的渲染问题能够尝试应用
CustomWrapper
组件包裹,它的作用是创立一个原生自定义组件。对后辈节点的setData
将由此自定义组件进行调用,达到部分更新的成果,从而晋升更新性能。 - video 组件有时在第一次渲染的时候获取不到时长,无奈自动播放问题。
- 伪元素和伪类在小程序端生效
- 全局款式问题,taro3 中如果不是 css module,则默认款式文件作用所有组件
Image
在 h5 的实现是有一层包装层,这点在 taro2 中同样存在ScrollView
中应用position: sticky
有效,这点在 taro2 中同样存在- hidden 异样,通过
state
数据管制显隐,不会失效。解决方案:通过三目运算解决