這是一篇針對舊版本用戶升級到 Taro Next 的遷移指南。因為本章內容包含了許多詳盡的闡述和遷移例子,所以看起來有一些長。但請不要擔心,Taro Next 大部分用法還是和舊版本一樣的。本章沒有提到的內容,你可以像舊版本的 Taro 一樣操作或使用。
事實上,你并不需要去更改任何業(yè)務的邏輯代碼,許多更改使用編輯器的「查找/替換」就可以完成。你甚至不需要完整地閱讀整章內容(重點在 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 中,我們把所有面向應用開發(fā)者的 API 都放在 @tarojs/taro
里,一個典型的 Taro 組件/頁面會像這樣:
// 類組件
import Taro, { Component } from '@tarojs/taro'
class Wallace extends Component {
componentDidMount () {
Taro.request().then(/* do something */)
}
render () {
return ...
}
}
// 函數式組件
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 ...
}
}
// 函數式組件
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 更小,多數情況性能表現也比 React 更好。但某些 React 生態(tài)的庫兼容性可能會出現問題。
在舊版本 Taro 中,頁面/項目的配置掛載在類組件的類屬性或函數式的屬性上,通過 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
}
一個完整的項目文件結構示例會像這樣:
.
├── app.config.js // 入口文件項目配置
├── app.js
├── app.scss
├── components
│ └── result.js // 組件若不使用第三方組件則無需配置
└── pages
└── index
├── index.config.js // index 的頁面配置
└── index.js
如果你需要引入 React 相關生態(tài)的庫,直接通過 npm install
安裝然后引入使用即可,Taro 不會再維護類似于 taro-redux
、taro-mobx
之類的庫。
// 當使用了 JSX 時,babel 會隱式地調用 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 對應的 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)
}
}
// 函數式組件
import { getCurrentInstance } from '@tarojs/taro'
function C () {
const { router } = getCurrentInstance()
// getCurrentInstance().router 和 useRouter 返回的內容也一樣
// const router = useRouter()
}
而對于項目入口組件而言,路由信息我們推薦在 componentDidShow
生命周期的參數中直接讀取。
// app.js 項目入口文件
class App extends Component {
componentDidShow (options /* 這里有你想要的路由信息 */) {
}
render () {
...
}
}
聰明的讀者已經猜到了,
getCurrentInstance().router
其實是訪問小程序當前頁面onLoad
生命周期參數的快捷方式。
在 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}
</>
}
舊版本文檔所提到的最佳實踐也不必再遵循。也就是說,即便你不給組件設置 defaultProps
,自定義事件名不以 on
開頭(還有其它的舊版本代碼風格最佳實踐),你的代碼也能運行。但值得注意的是,遵循這樣的 代碼風格最佳實踐 可以讓你的代碼更健壯,你的應用也會因此而收益。而對于另外的一些由于舊版本
Taro 執(zhí)行機制的 hack(例如 render 調用兩次,state 和 props 無法重名,不要打印組件),這類最佳實踐可以不必理會。
Taro Next 在底層會維護一個精簡的 DOM 系統(tǒng),在框架中使用 ref
鏈接到的是一個 Taro Element 實例,因此你直接可以使用 HTMLElement
的部分方法直接操作它。如果你需要獲取原生小程序 DOM 實例,那需要使用原生小程序的 SelectorQuery
來獲取。
大部分和渲染相關的 DOM 屬性你都可以通過像 Web 開發(fā)一樣獲取或設置(如果有必要的話你甚至可以通過
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ā)中常見做法
// 以下寫法也能更新視圖,但不推薦這么做,更推薦使用數據來驅動視圖更新
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ā),第一個參數 err
指向拋出的錯誤,第二個參數 info
是組件的調用信息。
componentDidCatch
和原有的componentDidCatchError
共同存在,區(qū)別在于componentDidCatchError
只能在入口組件(App)中使用,對應原生小程序的生命周期onError()
,componentDidCatch
可以在任何 React/Nerv 類組件中使用(包括入口組件)。
在 Taro Next,Taro 的專有 Hooks(例如 usePageScroll
, useReachBottom
)從 @tarojs/taro
中引入,框架自己的 Hooks (例如 useEffect
, useState
)從對應的框架引入。
另外,舊版本的 Taro 可以在 Class Component 中使用 Hooks,但 React 是不允許這樣的行為的。
import { usePageScroll, useReachBottom } from '@tarojs/taro' // Taro 專有 Hooks
import { useState, useEffect } from 'react' // 框架 Hooks (基礎 Hooks)
// 如果你使用 Nerv 的話
// import { useState, useEffect } from 'nervjs' // 框架 Hooks (基礎 Hooks)
由于 Taro Next 沒有自定義組件,所以也沒有了 this.$scope
和 this.$componentType
的概念。getCurrentInstance().page
可以返回當前小程序頁面的實例。
更多建議: