Taro 2升级Taro 3小结
背景
在2020年项目立项时,考虑到当时团队内部的技术栈主要是React,因此在最终的项目开发中使用了Taro来完成我们的小程序开发,而当时最新版本就是Taro 2.x;虽然随着这几年的版本迭代我们升级了几个小版本;但终究还是在Taro 2.x的范畴之中;而目前随着项目的迭代我们发现Taro 2.x对我们的开发体验越来越差,逐渐跟不上项目的迭代。因此萌生了将其升为3.x的想法。
在此记录一下升级中踩的坑(声明:我们项目中使用的是React)。
版本简介
Taro 3.x 相较于 Taro 1/2 编译时架构,Taro 3 采用了重运行时的架构,让开发者可以获得完整的 React / Vue 等框架的开发体验。具体原理请参考 《小程序跨框架开发的探索与实践》。
Taro 1/2 以及 Taro 3的实现原理可参考: taro1/2及taro3实现原理
Taro 1/2升级到Taro 3虽然看文档需要修改的地方有很多,但是其实大部分情况都可通过搜索替换即可完成的(主要任务量时测试同学的,哈哈)
版本升级
注意:升级之前记得先提交原来的代码。
官方文档:taro-docs.jd.com/docs/migrat…
1、根据官方文档中的升级指南升级相应的依赖
官方做法:
个人做法:
(推荐此做法) 为了更清楚的了解Taro 3相对于Taro 2的项目结构的差异:
- init了一个新的Taro 3项目;
- 根据初始化的项目中的依赖,删除掉旧项目中的相应依赖,使用新的依赖版本;
- 删除旧项目中的node_modules文件,重新执行npm install;
2、API和Hooks的修改
Taro 2中,所有的API都是在@tarojs/taro
中引入的;
而在Taro 3中,除了属于框架本身的 API 从框架自己的包中引入,其它的 API 仍然从 @tarojs/taro
引入。使用哪个框架来进行开发完全由开发者来决定。
对于我们来说修改项如下(旧的写法大家都知道,就不罗列了):
1、以函数式组件为例:
import Taro from '@tarojs/taro'
import { useState, useEffect } from 'react';
2、以类组件为例:
import Taro from '@tarojs/taro';
import React, { Component } from 'react';
注:当使用了 JSX 时,babel 会隐式地调用 React.createElement;因此只要你使用了 JSX,就要把 React 引入
3、项目以及页面配置
在Taro 2中,项目和页面的配置属性都是挂载在类属性或者函数式的属性上的。但是在Taro 3中项目配置以及每个页面的配置都是在自己的配置文件中去完成的。
参考一下之前我们init的Taro 3项目的文件结构
结构其实很明确,那我们就按照此结构重写我们项目中的相应配置,并且删除之前的写在index.tsx中的无用配置即可.
注:此处的*.config.ts文件名中的“*”需要和你的页面文件名保持一致
下面贴个官网提供的实例:
Taro 2写法:
// app.js 项目配置
class App extends Component {
config = {
pages: [
'pages/index/index'
],
window: {
backgroundTextStyle: 'light',
navigationBarBackgroundColor: '#fff',
navigationBarTitleText: 'WeChat',
navigationBarTextStyle: 'black'
}
}
render () {
return ...
}
}
// index.js 页面配置
function Index () {
return ...
}
Index.config = {
navigationBarTitleText: '首页'
}
Taro 3.x写法
// app.js 项目文件
class App extends Component {
render () {
return ...
}
}
// app.config.js
export default {
pages: [
'pages/index/index'
],
window: {
backgroundTextStyle: 'light',
navigationBarBackgroundColor: '#fff',
navigationBarTitleText: 'WeChat',
navigationBarTextStyle: 'black'
}
}
// index.js 页面文件
function Index () {
return ...
}
// index.config.js 页面配置
const title = '首页'
export default {
navigationBarTitleText: title
}
4、使用第三方React库
之前我们项目中使用的redux是这样引入的
import { connect } from '@tarojs/redux';
import { useSelector, useDispatch } from '@tarojs/redux';
现在由于taro不再维护@taro/redux
库,因此我们需要在react-redux
中去引入(记得install后再用),因此如下写法:
import { connect } from 'react-redux';
import { useSelector, useDispatch } from 'react-redux';
5、路由修改
旧版类组件的使用方式:this.$router
旧版函数组件使用方式:
import { useRouter } from '@tarojs/taro
const router = useRouter();
新版使用方式:
import { getCurrentInstance } from '@tarojs/taro'
const { router } = getCurrentInstance()
6、编译配置
这个没什么可说的,我们使用的是react,因此在配置中添加framework配置即可;
framework: 'react'
7、Ref & DOM
ref:之前我们项目中ref是可以以字符串的形式去创建的,项目举例:
Taro 3已经不支持此写法,正确写法如下:
// 函数式组件
import { useRef } from 'react';
const modalRef = useRef();
<Modal ref={modalRef} />
// 类组件
import React, { createRef } from 'react';
class TestComponent extends React.Component<IProps, IState>{
toast = createRef();
...
render(){
<Toast ref={this.toast} />
}
}
DOM:Taro 3 在底层会维护一个精简的 DOM 系统,在框架中使用 ref
链接到的是一个 Taro Element 实例,因此你直接可以使用 HTMLElement
的部分方法直接操作它。如果你需要获取原生小程序 DOM 实例,那需要使用原生小程序的 SelectorQuery
来获取。
注:大部分和渲染相关的 DOM 属性你都可以通过像 Web 开发一样获取或设置(如果有必要的话你甚至可以通过 parentNode
和 childNodes
访问元素的父元素和子元素!),但元素的位置你还是必须通过原生小程序 DOM 实例的 boundingClientRect()
和 scrollOffset()
方法获取。
8、生命周期
react生命周期更名:
componentWillMount()
->UNSAFE_componentWillMount()
componentWillReceiveProps
->UNSAFE_componentWillReceiveProps()
componentWillUpdate
->UNSAFE_componentWillUpdate()
新增一个生命周期: componentDidCatch(err, info)
,这是由框架本身(React 或 Nerv)提供的。componentDidCatch(err, info)
会在组件和它的子孙抛出错误时触发,第一个参数 err
指向抛出的错误,第二个参数 info
是组件的调用信息。(可借此做日志管理)
9、$scope
和 $componentType
Taro 3没有 this.$scope
和 this.$componentType
。getCurrentInstance().page
可以返回当前小程序页面的实例。
我们直接替换即可。
小结
对于之前项目中有使用CSS Modules 的人来说,经过上面简单的搜索替换添加等一系列操作后,我们的项目就已经能跑起来了(如果还跑不起来就根据控制台提示去补充修改即可),可以看到,虽然Taro 2升级Taro 3过程中我们要修改的地方很多,但其实细心看都是一些简单的搜索替换即可完成的事情,对于较小的项目,很容易就能完成Taro 版本的升级。但是对于内容较多的项目来说,更改的工作量就很大了。再此可以尝试使用一下官方推荐的一键升级工具:taro2-to-3,by @SyMind。
对于项目中之前没有使用CSS Modules 的人来说,最大的坑来了,继续往下看~~
10、样式问题
这个是我认为坑最大的一个。。。先看看文档怎么说的
在taro 3中,没有 组件的外部样式和全局样式 的概念,组件的配置(config.js
)是无效的,页面和入口文件引入的 CSS 都会变成全局 CSS ,没有了 externalClasses
和 addGlobalClass
这两个概念。
如果你需要带作用域的 CSS,可以考虑使用 CSS Modules。
好了,看了文档的解释我们再看看项目中的css怎么写的
我们这样的写法会导致,我们在修改完项目之后极大概率(除非保证类名不会重复的项目不会出现,但是不会吧,不会真有这样的项目吧?)会出现项目的样式错乱的问题。
好,那我们就使用文档推荐的CSS Modules吧? 无非就是在配置文件中加配置嘛!
cssModules: {
enable: true, // 默认为 false,如需使用 css modules 功能,则设为 true
config: {
namingPattern: 'module', // 转换模式,取值为 global/module
generateScopedName: '[name]__[local]___[hash:5]',
},
},
好,加完了!问题来了,项目中我们的写法都是形如className="XXXXX"
的方式。再看看CSS Modules怎么写的
import styles from "./style.less";
// import { className } from "./style.css";
element.innerHTML = '<div class="' + styles.className + '">';
都是styles.clssName
的命名方式
那我们要修改的岂不是很多~~ , 想想就崩溃了!那可以思考一下有没有什么简单的修改方式可以达成同样的目的呢?答案当然是有的,babel-plugin-react-css-modules
是不是很熟悉呢?没错,我们web项目中很常用的一个插件,我们可以通过它很便捷的将className修改为styleName即可达到快速实现修改CSS Modules的目的。
install之后,我们还需要如下配置
美滋滋,运行一下如下代码:
运行结果:
很明显失败了,原因是生成的类名不一致,既然使用hash会导致命名不一致的问题,那我们就曲线救国使用一下"[path][name]__[local]"命名方式吧。
再次运行:
完美配置生效,基于此我们就可以通过全局搜索替换的方式将className修改为styleName,从而降低我们的工作量。
总结
基于以上修改,我们已经完成了对于Taro 2升级Taro 3的操作,虽然已经完成了升级,但是还遗留了一个问题,那就是为什么两次生成的hash值不一致的问题,这个由于目前还没时间去深入探究,因此会留在下期去解答。有深入了解这块的小哥哥、小姐姐欢迎交流。
转载自:https://juejin.cn/post/7215991883801460792