Taro官網文件總結

nattsha發表於2019-09-01

最近一直在學習taro,網上搜的重點知識總結很少,所以想著自己寫一篇我覺得比較重要的知識點總結一下。

1.檔案組織形式

以下檔案組織規範為最佳實踐的建議

所有專案原始碼請放在專案根目錄 src 目錄下,專案所需最基本的檔案包括 入口檔案 以及 頁面檔案

  • 入口檔案為 app.js
  • 頁面檔案建議放置在 src/pages 目錄下

一個可靠的 Taro 專案可以按照如下方式進行組織

├── config                 配置目錄
|   ├── dev.js             開發時配置
|   ├── index.js           預設配置
|   └── prod.js            打包時配置
├── src                    原始碼目錄
|   ├── components         公共元件目錄
|   ├── pages              頁面檔案目錄
|   |   ├── index          index 頁面目錄
|   |   |   ├── banner     頁面 index 私有元件
|   |   |   ├── index.js   index 頁面邏輯
|   |   |   └── index.css  index 頁面樣式
|   ├── utils              公共方法庫
|   ├── app.css            專案總通用樣式
|   └── app.js             專案入口檔案
└── package.json複製程式碼

2.檔案命名

Taro 中普通 JS/TS 檔案以小寫字母命名,多個單詞以下劃線連線,例如 util.jsutil_helper.js

Taro 元件檔案命名遵循 Pascal 命名法,例如 ReservationCard.jsx

3.JavaScript 書寫規範

taro的書寫規範大概和Eslint的規範類似,具體可參考官網連結:taro-docs.jd.com/taro/docs/s…

4.書寫順序

在 Taro 元件中會包含類靜態屬性、類屬性、生命週期等的類成員,其書寫順序最好遵循以下約定(順序從上至下)

  1. static 靜態方法
  2. constructor
  3. componentWillMount
  4. componentDidMount
  5. componentWillReceiveProps
  6. shouldComponentUpdate
  7. componentWillUpdate
  8. componentDidUpdate
  9. componentWillUnmount
  10. 點選回撥或者事件回撥 比如 onClickSubmit() 或者 onChangeDescription()
  11. render

5.推薦使用物件解構的方式來使用 state、props

import Taro, { Component } from '@tarojs/taro'
import { View, Input } from '@tarojs/components'

class MyComponent extends Component {
  state = {
    myTime: 12
  }
  render () {
    const { isEnable } = this.props     // ✓ 正確
    const { myTime } = this.state     // ✓ 正確
    return (
      <View className='test'>
        {isEnable && <Text className='test_text'>{myTime}</Text>}
      </View>
    )
  }
}複製程式碼

6.不要在呼叫 this.setState 時使用 this.state

由於 this.setState 非同步的緣故,這樣的做法會導致一些錯誤,可以通過給 this.setState 傳入函式來避免

this.setState({
  value: this.state.value + 1
})   // ✗ 錯誤


this.setState(prevState => ({ value: prevState.value + 1 }))    // ✓ 正確複製程式碼

7.map 迴圈時請給元素加上 key 屬性

list.map(item => {
  return (
    <View className='list_item' key={item.id}>{item.name}</View>
  )
})複製程式碼

8.儘量避免在 componentDidMount 中呼叫 this.setState

因為在 componentDidMount 中呼叫 this.setState 會導致觸發更新

import Taro, { Component } from '@tarojs/taro'
import { View, Input } from '@tarojs/components'

class MyComponent extends Component {
  state = {
    myTime: 12
  }
  
  componentDidMount () {
    this.setState({     // ✗ 儘量避免,可以在 componentWillMount 中處理
      name: 1
    })
  }
  
  render () {
    const { isEnable } = this.props
    const { myTime } = this.state
    return (
      <View className='test'>
        {isEnable && <Text className='test_text'>{myTime}</Text>}
      </View>
    )
  }
}
複製程式碼

不要在 componentWillUpdate/componentDidUpdate/render 中呼叫 this.setState

import Taro, { Component } from '@tarojs/taro'
import { View, Input } from '@tarojs/components'

class MyComponent extends Component {
  state = {
    myTime: 12
  }
  
  componentWillUpdate () {
    this.setState({     // ✗ 錯誤
      name: 1
    })
  }
  
  componentDidUpdate () {
    this.setState({     // ✗ 錯誤
      name: 1
    })
  }
  
  render () {
    const { isEnable } = this.props
    const { myTime } = this.state
    this.setState({     // ✗ 錯誤
      name: 11
    })
    return (
      <View className='test'>
        {isEnable && <Text className='test_text'>{myTime}</Text>}
      </View>
    )
  }
}複製程式碼

9.事件繫結均以 on 開頭

在 Taro 中所有預設事件如 onClickonTouchStart 等等,均以 on 開頭

import Taro, { Component } from '@tarojs/taro'
import { View, Input } from '@tarojs/components'

class MyComponent extends Component {
  state = {
    myTime: 12
  }

  clickHandler (e) {
    console.log(e)
  }
  
  render () {
    const { myTime } = this.state

    return (
      <View className='test' onClick={this.clickHandler}>    // ✓ 正確
        <Text className='test_text'>{myTime}</Text>
      </View>
    )
  }
}複製程式碼

10.不能使用 Array#map 之外的方法操作 JSX 陣列

Taro 在小程式端實際上把 JSX 轉換成了字串模板,而一個原生 JSX 表示式實際上是一個 React/Nerv 元素(react-element)的構造器,因此在原生 JSX 中你可以隨意地對一組 React 元素進行操作。但在 Taro 中你只能使用 map 方法,Taro 轉換成小程式中 wx:for

11.設計稿及尺寸單位

在 Taro 中尺寸單位建議使用 px百分比 %,Taro 預設會對所有單位進行轉換。在 Taro 中書寫尺寸按照 1:1 的關係來進行書寫,即從設計稿上量的長度 100px,那麼尺寸書寫就是 100px,當轉成微信小程式的時候,尺寸將預設轉換為 100rpx,當轉成 H5 時將預設轉換為以 rem 為單位的值。

如果你希望部分 px 單位不被轉換成 rpx 或者 rem ,最簡單的做法就是在 px 單位中增加一個大寫字母,例如 Px 或者 PX 這樣,則會被轉換外掛忽略。

結合過往的開發經驗,Taro 預設以 750px 作為換算尺寸標準,如果設計稿不是以 750px 為標準,則需要在專案配置 config/index.js 中進行設定,例如設計稿尺寸是 640px,則需要修改專案配置 config/index.js 中的 designWidth 配置為 640

12.元件化 & Props

元件可以將 UI 切分成一些的獨立的、可複用的部件,這樣你就只需專注於構建每一個單獨的部件。

元件從概念上看就像是函式,它可以接收任意的輸入值(稱之為 props),並返回一個需要在頁面上展示的 Taro 元素。

Props 的只讀性

一個宣告的元件決不能修改它自己的 props

使用 PropTypes 檢查型別

隨著應用日漸龐大,你可以通過型別檢查捕獲大量錯誤。要檢查元件的屬性,你需要配置特殊的 propTypes 屬性:

import PropTypes from 'prop-types';

class Greeting extends Component {
  render() {
    return (
      <h1>Hello, {this.props.name}</h1>
    );
  }
}

Greeting.propTypes = {
  name: PropTypes.string
};複製程式碼

13.正確地使用 State

關於 setState() 這裡有三件事情需要知道:

1.不要直接更新狀態

例如,此程式碼不會重新渲染元件:

// Wrong
this.state.comment = 'Hello'
複製程式碼

應當使用 setState():

// Correct
this.setState({ comment: 'Hello' })複製程式碼

2.狀態更新一定是非同步的

Taro 可以將多個 setState() 呼叫合併成一個呼叫來提高效能。

因為 this.stateprops 一定是非同步更新的,所以你不能在 setState 馬上拿到 state 的值,例如:

// 假設我們之前設定了 this.state.counter = 0
updateCounter () {
  this.setState({
    counter: 1
  })
  console.log(this.state.counter) // 這裡 counter 還是 0
}
複製程式碼

正確的做法是這樣,在 setState 的第二個引數傳入一個 callback:

// 假設我們之前設定了 this.state.counter = 0
updateCounter () {
  this.setState({
    counter: 1
  }, () => {
    // 在這個函式內你可以拿到 setState 之後的值
  })
}複製程式碼

3.state 更新會被合併

當你呼叫 setState(),Taro 將合併你提供的物件到當前的狀態中。

例如,你的狀態可能包含幾個獨立的變數:

constructor(props) {
  super(props)
  this.state = {
    posts: [],
    comments: []
  }
}
複製程式碼

然後通過呼叫獨立的 setState() 呼叫分別更新它們:

componentDidMount() {
  fetchPosts().then(response => {
    this.setState({
      posts: response.posts
    });
  });

  fetchComments().then(response => {
    this.setState({
      comments: response.comments
    })
  })
}
複製程式碼

合併是淺合併,所以 this.setState({comments}) 不會改變 this.state.posts 的值,但會完全替換 this.state.comments 的值。

14.條件渲染

列舉條件渲染

有時渲染的條件非常多,不管是 if-else 還是 switch-case 來做條件渲染都會顯得太麻煩。這時我們可以使用「表驅動法」:列舉渲染。

function Loading (props) {
  const { loadingText, LOADING_STATUS, loadingStatus, onRetry } = props
  return (
    <View className='loading-status'>
      {
        {
          'loading': loadingText,
          'fail': <View onClick={onRetry}> 載入失敗, 點選重試 </View>,
          'no-more': '沒有更多了'
        }[loadingStatus] /** loadingStatus 是 `loading`、`fail`、`no-more`  其中一種狀態 **/
      }
    </View>
  )
}複製程式碼

15.渲染多個元件

下面,我們使用 JavaScript 中的 map() 方法遍歷 numbers 陣列。對陣列中的每個元素返回 <Text> 標籤,最後我們得到一個陣列 listItems

const numbers = [...Array(100).keys()] // [0, 1, 2, ..., 98, 99]
const listItems = numbers.map((number) => {
  return <Text className='li'> 我是第 {number + 1} 個數字</Text>
})
複製程式碼

這段程式碼生成了一個 1 到 100 的數字列表。

Keys

但是在上面的程式碼,你會得到一個報錯:提醒你當迴圈一個陣列時應該提供 keys。Keys 可以在 DOM 中的某些元素被增加或刪除的時候幫助 Nerv/小程式 識別哪些元素髮生了變化。因此你應當給陣列中的每一個元素賦予一個確定的標識。

taroKeys

taroKey 適用於迴圈渲染原生小程式元件,賦予每個元素唯一確定標識,轉換為小程式的 wx:key

元素的 key 在他的兄弟元素之間應該唯一

陣列元素中使用的 key 在其兄弟之間應該是獨一無二的。然而,它們不需要是全域性唯一的。當我們生成兩個不同的陣列時,我們可以使用相同的 key

16.類函式式元件

v1.3.0-beta.0 起支援

由於一個檔案不能定義兩個元件,但有時候我們需要元件內部的抽象元件,這時類函式式元件就是你想要答案。假設我們有一個 Class 元件,它包括了一個 Header 一個 Footer,我們可以這樣定義:

class SomePage extends Taro.Component {
  renderHeader () {
    const { header } = this.state
    return <View>{header}</View>
  }

  renderFooter (footer) {
    return <View>{footer}</View>
  }

  render () {
    return (
      <View>
        {this.renderHeader()}
        {...}
        {this.renderFooter('footer')}
      </View>
    )
  }
}
複製程式碼

renderHeaderrenderFooter 函式中,我們可以訪問類的 this,也可以傳入不限量的引數,這型別的函式也可以呼叫無限次數。但這樣的寫法也存在一些限制:

  1. 函式的命名必須以 render 開頭,render 後的第一個字母需要大寫
  2. 函式的引數不得傳入 JSX 元素或 JSX 元素引用
  3. 函式不能遞迴地呼叫自身

17.在我們設計元件時,有些元件通常不知道自己的子元件會有什麼內容,例如 SidebarDialog 這樣的容器元件。

我們建議在這樣的情況使用 this.props.children 來傳遞子元素:

class Dialog extends Component {
  render () {
    return (
      <View className='dialog'>
        <View className='header'>Welcome!</View>
        <View className='body'>
          {this.props.children}
        </View>
        <View className='footer'>-- divider --</View>
      </View>
    )
  }
}
複製程式碼

這樣就能允許其它元件在 JSX 中巢狀任意子元件傳遞給 Dialog:

class App extends Component {
  render () {
    return (
      <View className='container'>
        <Dialog>
          <View className="dialog-message">
            Thank you for using Taro.
          </View>
        </Dialog>
      </View>
    )
  }
}
複製程式碼

<Dialog /> JSX 標籤內的任何內容都會作為它的子元素(Children)都會傳遞到它的元件。

請不要對 this.props.children 進行任何操作。Taro 在小程式中實現這個功能使用的是小程式的 slot 功能,也就是說你可以把 this.props.children 理解為 slot 的語法糖,this.props.children 在 Taro 中並不是 React 的 ReactElement 物件,因此形如 this.props.children && this.props.childrenthis.props.children[0] 在 Taro 中都是非法的。

this.props.children 無法用 defaultProps 設定預設內容。由於小程式的限制,Taro 也無法知道元件的消費者是否傳入內容,所以無法應用預設內容。

不能把 this.props.children 分解為變數再使用。由於普通的 props 有一個確切的值,所以當你把它們分解為變數執行時可以處理,this.props.children 則不能這樣操作,你必須顯性地把 this.props.children 全部都寫完整才能實現它的功能。

組合

1.1.9 開始支援

有些情況你不僅僅需要只傳遞一個子元件,可能會需要很多個「佔位符」。例如在這個 Dialog 元件中,你不僅需要自定義它的 body,你希望它的 headerfooter 都是給 Dialog 元件的使用者自由定製。這種情況可以這樣做:

class Dialog extends Component {
  render () {
    return (
      <View className='dialog'>
        <View className='header'>
          {this.props.renderHeader}
        </View>
        <View className='body'>
          {this.props.children}
        </View>
        <View className='footer'>
          {this.props.renderFooter}
        </View>
      </View>
    )
  }
}

class App extends Component {
  render () {
    return (
      <View className='container'>
        <Dialog
          renderHeader={
            <View className='welcome-message'>Welcome!</View>
          }
          renderFooter={
            <Button className='close'>Close</Button>
          }
        >
          <View className="dialog-message">
            Thank you for using Taro.
          </View>
        </Dialog>
      </View>
    )
  }
}
複製程式碼

在我們宣告 Dialog 元件時,headerfooter 部分我們分別增加了 this.props.renderHeaderthis.props.renderFooter 作為佔位符。相應地,我們在使用 Dialog 元件時,就可以給 renderHeaderrenderFooter 傳入 JSX 元素,這兩個分別傳入的 JSX 元素將會填充它們在 Dialog 元件中的位置——就像在 Dialog JSX 標籤裡寫入的內容,會填充到 this.props.children 的位置一樣。

注意事項

元件的組合需要遵守 this.props.children 的所有規則。組合這個功能和 this.props.children 一樣是通過 slot 實現的,也就是說 this.props.children 的限制對於元件組合也都同樣適用。

所有組合都必須用 render 開頭,且遵守駝峰式命名法。和我們的事件規範以 on 開頭一樣,元件組合使用 render 開頭。

組合只能傳入單個 JSX 元素,不能傳入其它任何型別。當你需要進行一些條件判斷或複雜邏輯操作的時候,可以使用一個 Block 元素包裹住,然後在 Block 元素的裡面填充其它複雜的邏輯。


相關文章