QQ小程序 自定義組件擴(kuò)展

2020-07-02 14:09 更新

為了更好定制自定義組件的功能,可以使用自定義組件擴(kuò)展機(jī)制。

擴(kuò)展后的效果

// behavior.js
module.exports = Behavior({
  definitionFilter(defFields) {
    defFields.data.from = 'behavior'
  },
})


// component.js
Component({
  data: {
    from: 'component'
  },
  behaviors: [require('behavior.js')],
  ready() {
    console.log(this.data.from) // 此處會(huì)發(fā)現(xiàn)輸出 behavior 而不是 component
  }
})

通過(guò)例子可以發(fā)現(xiàn),自定義組件的擴(kuò)展其實(shí)就是提供了修改自定義組件定義段的能力,上述例子就是修改了自定義組件中的data 定義段里的內(nèi)容。

使用擴(kuò)展

Behavior() 構(gòu)造器提供了新的定義段 definitionFilter ,用于支持自定義組件擴(kuò)展。 definitionFilter 是一個(gè)函數(shù),在被調(diào)用時(shí)會(huì)注入兩個(gè)參數(shù),第一個(gè)參數(shù)是使用該 behavior 的 component/behavior 的定義對(duì)象,第二個(gè)參數(shù)是該 behavior 所使用的 behavior 的 definitionFilter 函數(shù)列表。 以下舉個(gè)例子來(lái)說(shuō)明:



// behavior3.js
module.exports = Behavior({
  definitionFilter(defFields, definitionFilterArr) {},
})


// behavior2.js
module.exports = Behavior({
  behaviors: [require('behavior3.js')],
  definitionFilter(defFields, definitionFilterArr) {
    // definitionFilterArr[0](defFields)
  },
})


// behavior1.js
module.exports = Behavior({
  behaviors: [require('behavior2.js')],
  definitionFilter(defFields, definitionFilterArr) {},
})


// component.js
Component({
  behaviors: [require('behavior1.js')],
})

上述代碼中聲明了1個(gè)自定義組件和3個(gè) behavior,每個(gè) behavior 都使用了 definitionFilter 定義段。那么按照聲明的順序會(huì)有如下事情發(fā)生:

  1. 當(dāng)進(jìn)行 behavior2 的聲明時(shí)就會(huì)調(diào)用 behavior3 的 definitionFilter 函數(shù),其中 defFields 參數(shù)是 behavior2 的定義段, definitionFilterArr 參數(shù)即為空數(shù)組,因?yàn)?behavior3 沒(méi)有使用其他的 behavior 。
  2. 當(dāng)進(jìn)行 behavior1 的聲明時(shí)就會(huì)調(diào)用 behavior2 的 definitionFilter 函數(shù),其中 defFields 參數(shù)是 behavior1 的定義段, definitionFilterArr 參數(shù)是一個(gè)長(zhǎng)度為1的數(shù)組,definitionFilterArr[0] 即為 behavior3 的 definitionFilter 函數(shù),因?yàn)?behavior2 使用了 behavior3。用戶在此處可以自行決定在進(jìn)行 behavior1 的聲明時(shí)要不要調(diào)用 behavior3 的 definitionFilter 函數(shù),如果需要調(diào)用,在此處補(bǔ)充代碼 definitionFilterArr0 即可,definitionFilterArr 參數(shù)會(huì)由基礎(chǔ)庫(kù)補(bǔ)充傳入。
  3. 同理,在進(jìn)行 component 的聲明時(shí)就會(huì)調(diào)用 behavior1 的 definitionFilter 函數(shù)。
  4. 簡(jiǎn)單概括,definitionFilter 函數(shù)可以理解為當(dāng) A 使用了 B 時(shí),A 聲明就會(huì)調(diào)用 B 的 definitionFilter 函數(shù)并傳入 A 的定義對(duì)象讓 B 去過(guò)濾。此時(shí)如果 B 還使用了 C 和 D ,那么 B 可以自行決定要不要調(diào)用 C 和 D 的 definitionFilter 函數(shù)去過(guò)濾 A 的定義對(duì)象。

真實(shí)案例

下面利用擴(kuò)展簡(jiǎn)單實(shí)現(xiàn)自定義組件的計(jì)算屬性功能:



// behavior.js
module.exports = Behavior({
  lifetimes: {
    created() {
      this._originalSetData = this.setData // 原始 setData
      this.setData = this._setData // 封裝后的 setData
    }
  },
  definitionFilter(defFields) {
    const computed = defFields.computed || {}
    const computedKeys = Object.keys(computed)
    const computedCache = {}


    // 計(jì)算 computed
    const calcComputed = (scope, insertToData) => {
      const needUpdate = {}
      const data = defFields.data = defFields.data || {}


      for (const key of computedKeys) {
        const value = computed[key].call(scope) // 計(jì)算新值
        if (computedCache[key] !== value) needUpdate[key] = computedCache[key] = value
        if (insertToData) data[key] = needUpdate[key] // 直接插入到 data 中,初始化時(shí)才需要的操作
      }


      return needUpdate
    }


    // 重寫 setData 方法
    defFields.methods = defFields.methods || {}
    defFields.methods._setData = function (data, callback) {
      const originalSetData = this._originalSetData // 原始 setData
      originalSetData.call(this, data, callback) // 做 data 的 setData
      const needUpdate = calcComputed(this) // 計(jì)算 computed
      originalSetData.call(this, needUpdate) // 做 computed 的 setData
    }


    // 初始化 computed
    calcComputed(defFields, true) // 計(jì)算 computed
  }
})

在組件中使用:

const beh = require('./behavior.js')
Component({
  behaviors: [beh],
  data: {
    a: 0,
  },
  computed: {
    b() {
      return this.data.a + 100
    },
  },
  methods: {
    onTap() {
      this.setData({
        a: ++this.data.a,
      })
    }
  }
})
<view>data: {{a}}</view>
<view>computed: {}</view>
<button bindtap="onTap">click</button>

實(shí)現(xiàn)原理很簡(jiǎn)單,對(duì)已有的 setData 進(jìn)行二次封裝,在每次 setData 的時(shí)候計(jì)算出 computed 里各字段的值,然后設(shè)到 data 中,已達(dá)到計(jì)算屬性的效果。

此實(shí)現(xiàn)只是作為一個(gè)簡(jiǎn)單案例來(lái)展示,請(qǐng)勿直接在生產(chǎn)環(huán)境中使用。

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

掃描二維碼

下載編程獅App

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

編程獅公眾號(hào)