Taro3介绍及升级记录

1,903 阅读6分钟

使用背景

随着各类小程序API不断的更新,现有的Taro2版本部分功能已不满足开发需求,例如Taro2对小程序独立分包不支持,那么Taro3通过配置项就能快速解决,像Taro3.3还添加了对html元素的支持。在这个背景下开始产生了萌发了使用Taro3的想法。

Taro原理对比

Taro2(重编译时,轻运行时)

分为两个部分,第⼀部分是编译时,第⼆部分是运⾏时。编译时会先对⽤户的React代码进⾏编译,转换成各个端上的⼩程序都可以运⾏的代码,然后再在各个⼩程序端上⾯都配上⼀个对应的运⾏时框架进⾏适配,最终让这份代码运⾏在各个⼩程序端上⾯。

image.png 编译时是使用 babel-parser 将 Taro 代码解析成抽象语法树,然后通过 babel-types 对抽象语法树进行一系列修改、转换操作,最后再通过 babel-generate 生成对应的目标代码。

缺点:

  1. Taro 对 JSX 的⽀持是通过编译时的适配去实现的,由于jsx的灵活性太强。不能100%支持jsx。
  2. 不⽀持 source-map,对源码进行编译转换之后。用户不方便使用和调试
  3. 运⾏时的缺陷。对于每个⼩程序平台,都会提供对应的⼀份运⾏时框架进⾏适配。当修改⼀些 Bug 或者新增⼀些特性的时候,需要同时去修改多份运⾏时框架。不宜维护

Taro3(重运行时)

Taro3则可以大致理解为解释型架构(相对于Taro1/2而言),主要通过在小程序端模拟实现DOM、BOM API来让前端框架直接运行在小程序环境中,从而达到小程序和H5统一的目的,而对于生命周期、组件库、API、路由等差异,依然可以通过定义统一标准,各端负责各自实现的方式来进行抹平。而正因为Taro3的原理,在Taro3中同时支持React、Vue等框架,甚至还支持了jQuery,还能支持让开发者自定义地去拓展其他框架的支持,比如Angular,Taro3整体架构如下

image.png

Taro3之后⼩程序端的整体架构。⾸先是⽤户的React或Vue的代码会通过CLI进⾏ Webpack 打包,其次在运⾏时会提供React和Vue对应的适配器进⾏适配,然后调⽤Taro提供的DOM和BOM API, 最后把整个程序渲染到所有的⼩程序端上⾯。

image.png

Taro3新特性

Taro3新特性参考文档:docs.taro.zone/blog/2021-0…

  1. 支持跨框架开发,React、Nerv、Vue 2、Vue 3
  2. 跨端:h5,微信,支付宝,百度,字节跳动等小程序
  3. 解决穿透问题,可以通过catchMove属性解决,<View catchMove/>,增加虚拟列表组件VirtualList,不限宽高,列表底部renderBottom={<View>我就是底线</View>};并同步更新了最新微信官方api
  4. 支持小程序独立分包功能,使用方式:在对应分包配置项中使用 independent: true即可,注意:独立分包中使用的代码不能使用依赖主包相应的工具类,否则报错

image.png

  1. 支持渲染Html字符串,可集成weui和antd Design Mobile

标签支持参考文档: github.com/NervJS/taro… 其中几个点说下:

- 元素标签转换规则:
  1. 由于小程序<Text>不能够嵌套<View>,<Image>等块级元素,和html <span>标签冲突,所以转化为view块级元素.需要将span开启默认行内样式,<i>等行内元素嵌套块级元素的写法不被兼容。所以在开发过程中需要规范写法。

  2. 表单元素checkbox/radio元素需要使用<CheckboxGroup>/<RadioGroup>包裹方可正常使用。下拉选择框select+option不做映射,直接使用代替,具体使用详见demo

image.png

- 元素属性转换规则:

image.png

- 事件映射

image.png

  • 部分h5样式兼容问题,对标签选择器不兼容,可通过标签类名:h5-${tagName} ,使用postcss插件处理后使用类选择器进行操作 。
  • 获取元素尺寸不兼容。h5获取是同步获取而小程序是异步获取,无法兼容,需要如下处理下:
*// h5*

const el = document.getElementById('#inner')

const res = el.getBoundingClientRect()

console.log(res)

*// 小程序*

const query = Taro.createSelectorQuery()

query.select('#inner')

.boundingClientRect()

.exec(res => {

console.log(res)

})
  • <canvas><video><audio>等DOM API差异

image.png

  • 部分样式或 CSS 选择器在小程序中不支持
  1. 通配符 *
  2. 媒体查询
  3. 属性选择器,当属性不是对应小程序组件的内置属性时

缺点

  1. 打包后文件过大,导致无法扫码预览
  2. 开启线上压缩打包后调试不方便
  3. taro1/2不能平滑升级到taro3.升级工作量大

Taro3 Demo:

1、 安装依赖:

npm intsall @tarojs/plugin-html antd-mobile@2.3.4

2、 在config下的index.js中 plugins中加入配置项:

['@tarojs/plugin-html', {

pxtransformBlackList: [/am-/, /demo-/, /^body/]

}]

3、 使用html例子

import Taro from "@tarojs/taro"
import { Component } from "react"
import { Button } from 'antd-mobile'
import { CheckboxGroup, RadioGroup, Picker, View } from "@tarojs/components"

import styles from './index.module.less'

export default class HtmlDemo extends Component {
    state = {
        selector: ['中国', '美国', '巴西', '日本'],
        selectorChecked: '中国',
        timeSel: '12:01',
        dateSel: '2018-04-22'
    }

    onChange = e => {
        this.setState({
        selectorChecked: this.state.selector[e.detail.value]
        })
    }

    render() {
        return (
        <div>
            <Button type='primary'>Ant-primary</Button>
            <a href="/ybsc/pages/order/index" target='_blank'>a标签使用</a>
            <div>div标签使用</div>
            <h5>h5标签中使用</h5>
            <p>p标签使用</p>
            <span>span标签使用</span>
            <br />
            <span>inputText标签使用:</span>
            <input type='text' className={styles.h5Input}></input>
            <span>inputPassword使用:</span>
            <input type='password' className={styles.h5Input}></input>
            <CheckboxGroup>
                box1:<input type='checkbox' className={styles.h5Input}></input>
                box2:<input type='checkbox' className={styles.h5Input}></input>
                box3:<input type='checkbox' className={styles.h5Input}></input>
            </CheckboxGroup>
            <br />
            <RadioGroup>
                rdio1:<input type='radio' name='daido' value='1'></input>
                rdio2:<input type='radio' name='radio' value='2'></input>
            </RadioGroup>

            <Picker mode='selector' range={this.state.selector} onChange={this.onChange}>
                <View className='picker'>
                当前选择:{this.state.selectorChecked}
                </View>
            </Picker>
        </div>
        )
    }
}

整体运行效果如下:

image.png

Taro2.0升级Taro3.0

升级前准备

由于升级过程中可能需要开发旧版本功能迭代。不能影响之前的开发环境,所以本次采用nvm(nvm使用文档)管理多个node版本,在不同的node环境中(如果已经安装过node需要卸载)。

windows安装nvm参考链接: blog.csdn.net/lewky_liu/a…

mac安装nvm参考链接:segmentfault.com/a/119000001…

升级步骤:全局升级taro,删除项目中node_modules和package-lock.json重新安装依赖。具体依赖如下:

  1. 升级全局环境:

    1. node: nvm install v14.17.0 ;
    2. tarojs: npm i -g @tarojs/cli@3.3.7 ;
  2. 升级项目所需依赖项

    1. runtime : npm i @tarojs/runtime@3.3.7
    2. mini-runner: npm i @tarojs/mini-runner@3.3.7
    3. components : npm i @tarojs/components@3.3.7
    4. taro : npm i@tarojs/taro@3.3.7
    5. webpack: npm i @tarojs/webpack-runner@3.3.7
    6. react: npm i react@17.0.0
    7. mobx: npm i mobx-react@6.7.1
    8. babel: npm i @babel/core@7.8.0 @babel/runtime@7.7.7 babel-preset-taro@3.3.7
    9. 新建babel.config.js,内容如下:
// https://github.com/NervJS/taro/blob/next/packages/babel-preset-taro/README.md
module.exports = {
    presets: [
        ['taro', {
            framework: 'react',// 开发语言框架
            ts: true // 是否支持ts
        }]
    ]
}
  1. 在项目根config 目录下的index.js文件中加上framework: 'react',配置项,配置开发框架(支持react, nerv, vue, vue3)
  2. 将已有的配置项app.jsx中分离出来放入app.config.js中,页面组件中每个config配置项需新建*.config.js配置文件将其配置项放入其中
  3. 将所有 Compontent从tarojs引入方式更换为从react导入。如下所示:
Component 导入方式由
import Taro, { Component } from '@tarojs/taro'
更换为react导
import Component from '@react'
  1. 如果使用了mobx管理全局状态,需要将所有从tarojs/mobx引入更换为 'mobx-react'
import {observer,inject} from 'tarojs/mobx => import {observer,inject} from 'mobx-react'
 
//如果注入变量为 const store = {userstore,toolstore} 在使用注入时应为:

<Provider store={store}>this.props.children}</Provider>

//获取时应为
const {userstore}= this.props.store
  1. 支持typescript (tsc -v 查看当前安装版本):
 npm install -g typescript
 npm install @typescript-eslint/eslint-plugin@4.15.1 --save-dev
 npm install @typescript-eslint/parser4.15.1 --save-dev

新建tsconfig.json文件/或用命令执行tsc --init 初始化 ,内容可根据需要选取配置:

{
    "compilerOptions": {
        "target": "es2017",
        "module": "commonjs",
        "removeComments": false,
        "preserveConstEnums": true,
        "moduleResolution": "node",
        "experimentalDecorators": true,
        "noImplicitAny": false,
        "allowSyntheticDefaultImports": true,
        "outDir": "lib",
        "noUnusedLocals": true,
        "noUnusedParameters": true,
        "strictNullChecks": true,
        "sourceMap": true,
        "baseUrl": ".",
        "rootDir": ".",
        "jsx": "react-jsx",
        "allowJs": true,
        "resolveJsonModule": true,
        "typeRoots": [
            "node_modules/@types",
            "global.d.ts"
        ]
    },
    "exclude": [
        "node_modules",
        "dist"
    ],
    "compileOnSave": false
}

升级后遇到的问题

升级后遇到得问题(报错问题)

  1. 问题:TypeError: Cannot call a class as a function。

    原因: Compontent没有从react引入
    解决:将Compontent从react中引入
  1. 问题:taro3 does not contain an export named :
    原因:导出组件方式为单个
    
    解决: 
        组件引入方式由 
        import { XxxxxXxxx } from '../../component/xxxxxx' 
        变为 
        import XxxxxXxxx from '../../component/xxxxxx' 
        方式引入或者导出文件采用多文件对象导出方式
  1. 问题:Cannot read property this.$router of undefined
    原因:this.$router属性删除。
    
    解决:将this.$router获取路由参数的方式需更换为 getCurrentInstance().router方式获取,
    getCurrentInstance从tarojs中导入
  1. 问题:项目组件config中配置顶部栏样式不生效
    原因:移除在组件中config配置项
    
    解决:需要在组件页面文件中使用config配置需新建* *.config.js文件中导出(注意 ** 表示文件目录名称与文件入口同级)
  1. 问题:没有使用 .module.less 创建样式,在使用同名的类时,样式错乱
    原因:没有使用 *.module.less 创建样式文件会挂载到全局作用域中*
    解决:未使用css module的页面,对父元素加一个唯一class进行包裹或者改写未css模块化包裹
  1. 问题:在组件中定义的样式或者元素选择器样式不生效
    原因:taro2编译后的和taro3编译的dom节点有差异,如<Header/>taro2编译后:<header>....</header>。
    taro3编译后为....,导致header上的定义的样式找不到
    解决:组件使用时在外包裹一层view.如<view><header/></view>,将样式移动至外层view中处理
  1. 问题:页面中点击跳转无反应
    原因:taro升级后数据接口改变导致。用taro2方式获取不到

    解决方案,分两种情况:

        1.  点击页面报错this.$router.params undefined:将this.$router.* 获取方式变更为getCurrentInstance().router.*获取//import { getCurrentInstance } from '@tarojs/taro'
        2.  this丢失,组件中添加的事件点击无反应或者绑定的事件没反应: 将 onClick={this._handleBtn} 更换为 onClick={(e) => { this._handleBtn(e) }}即可(或使用bind改变this指向)(或采用hooks写法)
  1. 问题:开发环境阅览时无法阅览
    解决:开发时启用生产压缩 set NODE_ENV=production && taro build --type weapp --watch
  1. 问题:chunk common [mini-css-extract-plugin] 开发启动警告 :
    解决方案:搜索使用组件的位置,将引入顺序更换为正确优先级导入
  1. 编译警告: 'mobx' does not contain an export named '_allowStateReadsEnd'.;

'mobx' does not contain an export named '_allowStateReadsStart'.

解决方案:安装依赖问题,删除依赖重新安装,删除package-lock.json ,mobx-react版本和官方创建模板保持一致
  1. 问题:Cannot set property 'state' of undefined、
    原因:组件中使用了constructor()没有调用super(props)
    解决:在constructor中首行加入super(props)
  1. 问题:关于原生属性dataset获取不到问题(不能支持用户自定义设置 data-*属性
    原因:taro3数据结构发生改变
    解决:参考[event.atrget.dataset获取不到问题issue](https://github.com/NervJS/taro/issues/7313),如果需要,则通过document.getElementById('xxx').prop获取
  1. 问题:event数据结构发生改变。获取对应的属性需更换 taro2 event数据结构如下:

image.png taro3后event数据结构如下:

image.png

点击触发事件后,taro2通过e.target获取target中dataset等属性,taro3 target属性包含在mpEvent中,并且target中的dataset是空。taro3需要避免在target中获取dataset属性

参考链接

  1. Taro3.0官方文档
  2. Taro github地址入口
  3. taro3参考文章
  4. taro3官方博客说明文档
  5. windows安装nvm参考链接: blog.csdn.net/lewky_liu/a…
  6. mac安装nvm参考链接:segmentfault.com/a/119000001…