這是一篇針對舊版本用戶升級到 Taro Next 的遷移指南。因為本章內(nèi)容包含了許多詳盡的闡述和遷移例子,所以看起來有一些長。但請不要擔心,Taro Next 大部分用法還是和舊版本一樣的。本章沒有提到的內(nèi)容,你可以像舊版本的 Taro 一樣操作或使用。
事實上,你并不需要去更改任何業(yè)務(wù)的邏輯代碼,許多更改使用編輯器的「查找/替換」就可以完成。你甚至不需要完整地閱讀整章內(nèi)容(重點在 1,2 小節(jié), API 和 項目/頁面配置),只有當出問題時定位到具體的小節(jié)即可。
更新到 Taro Next 首先需要更新項目依賴:
# 更新 CLI
$ npm i -g @tarojs/cli@next
# 在項目目錄更新項目依賴
$ npm i @tarojs/runtime@next @tarojs/mini-runner@next @tarojs/components@next @tarojs/taro@next
$ npm i react @tarojs/react@next # 如果使用 React
$ npm i nervjs # 如果使用 Nerv
# CLI 命令和以前一模一樣
$ taro build --type weapp --watch
在舊版本 Taro 中,我們把所有面向應(yīng)用開發(fā)者的 API 都放在 @tarojs/taro
里,一個典型的 Taro 組件/頁面會像這樣:
// 類組件
import Taro, { Component } from '@tarojs/taro'
class Wallace extends Component {
componentDidMount () {
Taro.request().then(/* do something */)
}
render () {
return ...
}
}
// 函數(shù)式組件
import Taro, { useEffect } from '@tarojs/taro'
function Tall () {
useEffect(() => {
Taro.request().then(/* do something */)
}, [])
return ...
}
在 Taro Next 中,屬于框架本身的 API 從框架自己的包中引入,其它的 API 仍然從 @tarojs/taro
引入。使用哪個框架來進行開發(fā)完全由開發(fā)者來決定。
import Taro from '@tarojs/taro'
import React, { Component } from 'react' // Component 是來自于 React 的 API
// 從 nervjs 中引入,那運行的就是 Nerv
// import { Component } from 'nervjs'
class Reporter extends Component {
componentDidMount () {
Taro.request().then(/* do something */)
}
render () {
return ...
}
}
// 函數(shù)式組件
import Taro from '@tarojs/taro'
// useEffect 是來自于 React 的 API
import React, { useEffect } from 'react'
// 從 nervjs 中引入,那運行的就是 Nerv
// import { useEffect } from 'nervjs'
function Fast () {
useEffect(() => {
Taro.request().then(/* do something */)
}, [])
return ...
}
Nerv 是凹凸實驗室的一個開源類 React 框架,體積比 React 更小,多數(shù)情況性能表現(xiàn)也比 React 更好。但某些 React 生態(tài)的庫兼容性可能會出現(xiàn)問題。
在舊版本 Taro 中,頁面/項目的配置掛載在類組件的類屬性或函數(shù)式的屬性上,通過 AST 分析取出來,然后生成 JSON 文件。但這樣做,項目頁面的配置就無法動態(tài)地生成:
// 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 Next 中,會有一個新的文件:*.config.js
,*
代表你頁面/項目文件的文件名,config
文件必須和頁面/項目文件在同一文件夾。在這個文件里你可以使用任意合法的 JavaScript 語法,只要最終把配置作為對象通過 export default
出去即可:
// 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
}
一個完整的項目文件結(jié)構(gòu)示例會像這樣:
.
├── app.config.js // 入口文件項目配置
├── app.js
├── app.scss
├── components
│ └── result.js // 組件若不使用第三方組件則無需配置
└── pages
└── index
├── index.config.js // index 的頁面配置
└── index.js
如果你需要引入 React 相關(guān)生態(tài)的庫,直接通過 npm install
安裝然后引入使用即可,Taro 不會再維護類似于 taro-redux
、taro-mobx
之類的庫。
// 當使用了 JSX 時,babel 會隱式地調(diào)用 React.createElement
// 因此只要你使用了 JSX,就要把 React 或 Nerv 引入
import React from 'react'
import { useSelector } from 'react-redux'
// 如果是使用的是 Nerv
// import { useSelector } from 'nerv-redux'
function Excited () {
const counter = useSelector(state => state.counter)
return ...
}
在舊版本中可以通過 this.$router
訪問當前組件/頁面路由的詳情。在 Taro Next 對應(yīng)的 API 是在 @tarojs/taro
中的 getCurrentInstance().router
,兩者的屬性一模一樣。
import { getCurrentInstance } from '@tarojs/taro'
class C extends Component {
current = getCurrentInstance()
componentWillMount () {
// getCurrentInstance().router 和 this.$router 和屬性一樣
console.log(this.current.router)
}
}
// 函數(shù)式組件
import { getCurrentInstance } from '@tarojs/taro'
function C () {
const { router } = getCurrentInstance()
// getCurrentInstance().router 和 useRouter 返回的內(nèi)容也一樣
// const router = useRouter()
}
而對于項目入口組件而言,路由信息我們推薦在 componentDidShow
生命周期的參數(shù)中直接讀取。
// app.js 項目入口文件
class App extends Component {
componentDidShow (options /* 這里有你想要的路由信息 */) {
}
render () {
...
}
}
聰明的讀者已經(jīng)猜到了,
getCurrentInstance().router
其實是訪問小程序當前頁面onLoad
生命周期參數(shù)的快捷方式。
在 Taro Next 中,沒有組件的外部樣式和全局樣式的概念,組件的配置(config.js
)是無效的,頁面和入口文件引入的 CSS 都會變成全局 CSS ,沒有了 externalClasses
和 addGlobalClass
這兩個概念。
如果你需要帶作用域的 CSS,可以考慮使用 CSS Modules。
jsxAttributeNameReplace 配置已被移除。因為我們不需要配置 externalClasses
,這個屬性也失去了它存在的意義。
Webpack
升級到 Webpack@4
,Babel
升級到 babel@7
。Webpack 升級是在 taro@2
中完成的,如果你是從 taro@1
升級上來的話,或許需要去看看 Taro 2 更改 查看使用
Webpack 編譯后帶來的變化。
升級到 babel@7
意味著你的項目文件全部都會通過根目錄的 babel.config.js
的配置進行編譯。
eslint-plugin-taro
已被廢棄,你不再需要遵循它所規(guī)定的種種限制。你可以發(fā)揮你的創(chuàng)造力使用任何合法的 JSX 語法:
import React from 'react'
import { View, Text } from '@tarojs/components'
function C () {
// 你可以選擇不使用 JSX,但元素還是必須從 `@tarojs/components` 引入
const title = React.createElement(View, null, 'Numbers:')
const numbers = []
for (let i = 0; i < 10; i++) {
numbers.push(<Text key={i}>{i}</Text>)
}
return <>
{title}
{numbers}
</>
}
舊版本文檔所提到的最佳實踐也不必再遵循。也就是說,即便你不給組件設(shè)置 defaultProps
,自定義事件名不以 on
開頭(還有其它的舊版本代碼風格最佳實踐),你的代碼也能運行。但值得注意的是,遵循這樣的 代碼風格最佳實踐 可以讓你的代碼更健壯,你的應(yīng)用也會因此而收益。而對于另外的一些由于舊版本
Taro 執(zhí)行機制的 hack(例如 render 調(diào)用兩次,state 和 props 無法重名,不要打印組件),這類最佳實踐可以不必理會。
Taro Next 在底層會維護一個精簡的 DOM 系統(tǒng),在框架中使用 ref
鏈接到的是一個 Taro Element 實例,因此你直接可以使用 HTMLElement
的部分方法直接操作它。如果你需要獲取原生小程序 DOM 實例,那需要使用原生小程序的 SelectorQuery
來獲取。
大部分和渲染相關(guān)的 DOM 屬性你都可以通過像 Web 開發(fā)一樣獲取或設(shè)置(如果有必要的話你甚至可以通過
parentNode
和childNodes
訪問元素的父元素和子元素?。?,但元素的位置你還是必須通過原生小程序 DOM 實例的boundingClientRect()
和scrollOffset()
方法獲取。
另外,如果你使用的是 React,就將無法使用字符串的形式來使用 ref
。(Nerv 不受此影響)
class C extends Components {
input = React.createRef()
componentDidMount () {
const node = this.input.current // node 是一個 Taro Element 實例
node.focus() // ok, 在 Web 開發(fā)中常見做法
// 以下寫法也能更新視圖,但不推薦這么做,更推薦使用數(shù)據(jù)來驅(qū)動視圖更新
node.setProerty('class', 'input-css-class')
node.className = 'input-css-class'
node.style.fontSize = '16px'
node.value = 'excited!'
// 如果你需要獲取原生小程序 DOM 的話
const miniNode = Taro.createSelectorQuery().select('#' + node.id)
}
render () {
return <Input ref={this.input} />
}
}
在未來,我們可能會在 Taro Element 上提供一個可以快速訪問小程序 DOM 實例的屬性。目前請按照上述例子使用。
當你使用 React 時(使用 Nerv 不受此影響),以下生命周期被更名:
componentWillMount()
-> UNSAFE_componentWillMount()
componentWillReceiveProps
-> UNSAFE_componentWillReceiveProps()
componentWillUpdate
-> UNSAFE_componentWillUpdate()
新增一個生命周期: componentDidCatch(err, info)
,這是由框架本身(React 或 Nerv)提供的。componentDidCatch(err, info)
會在組件和它的子孫拋出錯誤時觸發(fā),第一個參數(shù) err
指向拋出的錯誤,第二個參數(shù) info
是組件的調(diào)用信息。
componentDidCatch
和原有的componentDidCatchError
共同存在,區(qū)別在于componentDidCatchError
只能在入口組件(App)中使用,對應(yīng)原生小程序的生命周期onError()
,componentDidCatch
可以在任何 React/Nerv 類組件中使用(包括入口組件)。
在 Taro Next,Taro 的專有 Hooks(例如 usePageScroll
, useReachBottom
)從 @tarojs/taro
中引入,框架自己的 Hooks (例如 useEffect
, useState
)從對應(yīng)的框架引入。
另外,舊版本的 Taro 可以在 Class Component 中使用 Hooks,但 React 是不允許這樣的行為的。
import { usePageScroll, useReachBottom } from '@tarojs/taro' // Taro 專有 Hooks
import { useState, useEffect } from 'react' // 框架 Hooks (基礎(chǔ) Hooks)
// 如果你使用 Nerv 的話
// import { useState, useEffect } from 'nervjs' // 框架 Hooks (基礎(chǔ) Hooks)
由于 Taro Next 沒有自定義組件,所以也沒有了 this.$scope
和 this.$componentType
的概念。getCurrentInstance().page
可以返回當前小程序頁面的實例。
更多建議: