最佳實(shí)踐

2020-05-12 17:46 更新

關(guān)于 JSX 支持程度補(bǔ)充說(shuō)明

由于 JSX 中的寫(xiě)法千變?nèi)f化,我們不能支持到所有的 JSX 寫(xiě)法,同時(shí)由于微信小程序端的限制,也有部分 JSX 的優(yōu)秀用法暫時(shí)不能得到很好地支持,特在此補(bǔ)充說(shuō)明一下對(duì)于 JSX 的支持程度:

以上的規(guī)則在 Taro 默認(rèn)生成的模板都有 ESLint 檢測(cè),無(wú)需做任何配置。如果你的編輯器沒(méi)有安裝 ESLint 插件可以參考以下教程在你的編輯器安裝:

默認(rèn)情況下 Taro 的編譯器也會(huì)對(duì)無(wú)法運(yùn)行的代碼進(jìn)行警告,當(dāng)沒(méi)有調(diào)用棧信息時(shí)代碼是可以生成的。如果你需要在編譯時(shí)禁用掉 ESLint 檢查,可以在命令前加入 ESLINT=false 參數(shù),例如:

$ ESLINT=false taro build --type weapp --watch

最佳編碼方式

經(jīng)過(guò)較長(zhǎng)時(shí)間的探索與驗(yàn)證,目前 Taro 在微信小程序端是采用依托于小程序原生自定義組件系統(tǒng)來(lái)設(shè)計(jì)實(shí)現(xiàn) Taro 組件化的,所以目前小程序端的組件化會(huì)受到小程序原生組件系統(tǒng)的限制,而同時(shí)為了實(shí)現(xiàn)以 React 方式編寫(xiě)代碼的目標(biāo),Taro 本身做了一些編譯時(shí)以及運(yùn)行時(shí)的處理,這樣也帶來(lái)了一些值得注意的約束,所以有必要闡述一下 Taro 編碼上的最佳實(shí)踐。

組件樣式說(shuō)明

微信小程序的自定義組件樣式默認(rèn)是不能受外部樣式影響的,例如在頁(yè)面中引用了一個(gè)自定義組件,在頁(yè)面樣式中直接寫(xiě)自定義組件元素的樣式是無(wú)法生效的。這一點(diǎn),在 Taro 中也是一樣,而這也是與大家認(rèn)知的傳統(tǒng) Web 開(kāi)發(fā)不太一樣。

給組件設(shè)置 defaultProps

在微信小程序端的自定義組件中,只有在 properties 中指定的屬性,才能從父組件傳入并接收

Component({
  properties: {
    myProperty: { // 屬性名
      type: String, // 類(lèi)型(必填),目前接受的類(lèi)型包括:String, Number, Boolean, Object, Array, null(表示任意類(lèi)型)
      value: '', // 屬性初始值(可選),如果未指定則會(huì)根據(jù)類(lèi)型選擇一個(gè)
      observer: function (newVal, oldVal, changedPath) {
         // 屬性被改變時(shí)執(zhí)行的函數(shù)(可選),也可以寫(xiě)成在 methods 段中定義的方法名字符串, 如:'_propertyChange'
         // 通常 newVal 就是新設(shè)置的數(shù)據(jù), oldVal 是舊數(shù)據(jù)
      }
    },
    myProperty2: String // 簡(jiǎn)化的定義方式
  }
  ...
})

而在 Taro 中,對(duì)于在組件代碼中使用到的來(lái)自 props 的屬性,會(huì)在編譯時(shí)被識(shí)別并加入到編譯后的 properties 中,暫時(shí)支持到了以下寫(xiě)法

this.props.property

const { property } = this.props

const property = this.props.property

但是一千個(gè)人心中有一千個(gè)哈姆雷特,不同人的代碼寫(xiě)法肯定也不盡相同,所以 Taro 的編譯肯定不能覆蓋到所有的寫(xiě)法,而同時(shí)可能會(huì)有某一屬性沒(méi)有使用而是直接傳遞給子組件的情況,這種情況是編譯時(shí)無(wú)論如何也處理不到的,這時(shí)候就需要大家在編碼時(shí)給組件設(shè)置 defaultProps 來(lái)解決了。

組件設(shè)置的 defaultProps 會(huì)在運(yùn)行時(shí)用來(lái)彌補(bǔ)編譯時(shí)處理不到的情況,里面所有的屬性都會(huì)被設(shè)置到 properties 中初始化組件,正確設(shè)置 defaultProps 可以避免很多異常的情況的出現(xiàn)。

組件傳遞函數(shù)屬性名以 on 開(kāi)頭

在 v1.3.0-beta.0 之后,自定義組件間的事件傳遞可以不用 on 開(kāi)頭,但內(nèi)置組件的事件依然是以 on 開(kāi)頭的,為了一致性我們?nèi)匀煌扑]你以 on 開(kāi)頭命名你的事件。

在 Taro 中,父組件要往子組件傳遞函數(shù),屬性名必須以 on 開(kāi)頭

// 調(diào)用 Custom 組件,傳入 handleEvent 函數(shù),屬性名為 onTrigger
class Parent extends Component {

  handleEvent () {

  }

  render () {
    return (
      <Custom onTrigger={this.handleEvent}></Custom>
    )
  }
}

這是因?yàn)?,微信小程序端組件化是不能直接傳遞函數(shù)類(lèi)型給子組件的,在 Taro 中是借助組件的事件機(jī)制來(lái)實(shí)現(xiàn)這一特性,而小程序中傳入事件的時(shí)候?qū)傩悦麑?xiě)法為 bindmyevent 或者 bind:myevent

<!-- 當(dāng)自定義組件觸發(fā) myevent 事件時(shí),調(diào)用 onMyEvent 方法 -->
<component-tag-name bindmyevent="onMyEvent" />
<!-- 或者可以寫(xiě)成 -->
<component-tag-name bind:myevent="onMyEvent" />

所以 Taro 中約定組件傳遞函數(shù)屬性名以 on 開(kāi)頭,同時(shí)這也和內(nèi)置組件的事件綁定寫(xiě)法保持一致了。

小程序端不要在組件中打印傳入的函數(shù)

自 v1.3.0-beta.0 沒(méi)有這條限制

前面已經(jīng)提到小程序端的組件傳入函數(shù)的原理,所以在小程序端不要在組件中打印傳入的函數(shù),因?yàn)槟貌坏浇Y(jié)果,但是 this.props.onXxx && this.props.onXxx() 這種判斷函數(shù)是否傳入來(lái)進(jìn)行調(diào)用的寫(xiě)法是完全支持的。

小程序端不要將在模板中用到的數(shù)據(jù)設(shè)置為 undefined

由于小程序不支持將 data 中任何一項(xiàng)的 value 設(shè)為 undefined ,在 setState 的時(shí)候也請(qǐng)避免這么用。你可以使用 null 來(lái)替代。

小程序端不要在組件中打印 this.props.children

在微信小程序端是通過(guò) <slot /> 來(lái)實(shí)現(xiàn)往自定義組件中傳入元素的,而 Taro 利用 this.props.children 在編譯時(shí)實(shí)現(xiàn)了這一功能, this.props.children 會(huì)直接被編譯成 <slot /> 標(biāo)簽,所以它在小程序端屬于語(yǔ)法糖的存在,請(qǐng)不要在組件中打印它。

支持 props 傳入 JSX

自 1.1.9 開(kāi)始支持

支持 props 傳入 JSX,但是元素傳入 JSX 的屬性名必須以 render 開(kāi)頭

例如,子組件寫(xiě)法

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>
    )
  }
}

父組件調(diào)用子組件時(shí)傳入 JSX

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>
    )
  }
}

組件屬性傳遞注意

不要以 id、class、style 作為自定義組件的屬性與內(nèi)部 state 的名稱(chēng),因?yàn)檫@些屬性名在微信小程序小程序中會(huì)丟失。

組件 state 與 props 里字段重名的問(wèn)題

不要在 state 與 props 上用同名的字段,因?yàn)檫@些字段在微信小程序中都會(huì)掛在 data 上。

小程序中頁(yè)面生命周期 componentWillMount 不一致問(wèn)題

由于微信小程序里頁(yè)面在 onLoad 時(shí)才能拿到頁(yè)面的路由參數(shù),而頁(yè)面 onLoad 前組件都已經(jīng) attached 了。因此頁(yè)面的 componentWillMount 可能會(huì)與預(yù)期不太一致。例如:

// 錯(cuò)誤寫(xiě)法
render () {
  // 在 willMount 之前無(wú)法拿到路由參數(shù)
  const abc = this.$router.params.abc
  return <Custom adc={abc} />
}

// 正確寫(xiě)法
componentWillMount () {
  const abc = this.$router.params.abc
  this.setState({
    abc
  })
}
render () {
  // 增加一個(gè)兼容判斷
  return this.state.abc && <Custom adc={abc} />
}

對(duì)于不需要等到頁(yè)面 willMount 之后取路由參數(shù)的頁(yè)面則沒(méi)有任何影響。

組件的 constructor 與 render 提前調(diào)用

很多細(xì)心的開(kāi)發(fā)者應(yīng)該已經(jīng)注意到了,在 Taro 編譯到小程序端后,組件的 constructor 與 render 默認(rèn)會(huì)多調(diào)用一次,表現(xiàn)得與 React 不太一致。

這是因?yàn)?,Taro 的組件編譯后就是小程序的自定義組件,而小程序的自定義組件的初始化時(shí)是可以指定 data 來(lái)讓組件擁有初始化數(shù)據(jù)的。開(kāi)發(fā)者一般會(huì)在組件的 constructor 中設(shè)置一些初始化的 state,同時(shí)也可能會(huì)在 render 中處理 state 與 props 產(chǎn)生新的數(shù)據(jù),在 Taro 中多出的這一次提前調(diào)用,就是為了收集組件的初始化數(shù)據(jù),給自定義組件提前生成 data ,以保證組件初始化時(shí)能帶有數(shù)據(jù),讓組件初次渲染正常。

所以,在編碼時(shí),需要在處理數(shù)據(jù)的時(shí)候做一些容錯(cuò)處理,這樣可以避免在 constructor 與 render 提前調(diào)用時(shí)出現(xiàn)由于沒(méi)有數(shù)據(jù)導(dǎo)致出錯(cuò)的情況。

JS 編碼必須用單引號(hào)

在 Taro 中,JS 代碼里必須書(shū)寫(xiě)單引號(hào),特別是 JSX 中,如果出現(xiàn)雙引號(hào),可能會(huì)導(dǎo)致編譯錯(cuò)誤。

環(huán)境變量 process.env 的使用

不要以解構(gòu)的方式來(lái)獲取通過(guò) env 配置的 process.env 環(huán)境變量,請(qǐng)直接以完整書(shū)寫(xiě)的方式 process.env.NODE_ENV 來(lái)進(jìn)行使用

// 錯(cuò)誤寫(xiě)法,不支持
const { NODE_ENV = 'development' } = process.env
if (NODE_ENV === 'development') {
  ...
}

// 正確寫(xiě)法
if (process.env.NODE_ENV === 'development') {

}

使用 this.$componentType 來(lái)判斷當(dāng)前 Taro.Component 是頁(yè)面還是組件

this.$componentType 可能取值分別為 PAGE 和 COMPONENT,開(kāi)發(fā)者可以根據(jù)此變量的取值分別采取不同邏輯。

原生小程序組件傳遞 props 給 Taro 組件

Taro v1.3+ 對(duì) props 系統(tǒng)進(jìn)行了改造,使得不能兼容原生組件通過(guò) properties 傳入的屬性。

目前可以通過(guò)把所有需要傳入 Taro 組件的 props,通過(guò)借助 extraProps 屬性來(lái)解決。

// 小程序組件常規(guī) props 傳遞
<plugin title="{{name}}" desc="{{desc}}" bindonclick="onClick"></plugin>

// 原生小程序組件調(diào)用 Taro 組件時(shí)需要改造成以下形式:
this.setData({
  extraProps: {
    name,
    desc,
    onClick: this.onClick
  }
})
<plugin extraProps="{{extraProps}}" ></plugin>

全局變量

在 Taro 中推薦使用 Redux 來(lái)進(jìn)行全局變量的管理,但是對(duì)于一些小型的應(yīng)用, Redux 就可能顯得比較重了,這時(shí)候如果想使用全局變量,推薦如下使用。

新增一個(gè)自行命名的 JS 文件,例如 global_data.js,示例代碼如下

const globalData = {}

export function set (key, val) {
  globalData[key] = val
}

export function get (key) {
  return globalData[key]
}

隨后就可以在任意位置進(jìn)行使用啦

import { set as setGlobalData, get as getGlobalData } from './path/name/global_data'

setGlobalData('test', 1)

getGlobalData('test')


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)