自定义组件扩展

为了更好定制自定义组件的功能,可以使用自定义组件扩展机制。

从小程序基础库版本 2.0.5 开始支持。

扩展后的效果

为了更好的理解扩展后的效果,在此举例说明:

代码示例

在开发者工具中打开

在开发者工具中打开

在 WEB IDE 中打开

  1. // behavior.js
  2. module.exports = Behavior({
  3. definitionFilter(defFields) {
  4. defFields.data.from = 'behavior'
  5. }
  6. })
  7. // component.js
  8. Component({
  9. data: {
  10. from: 'component'
  11. },
  12. behaviors: [require('./behavior.js')],
  13. ready() {
  14. // 此处会发现输出 behavior 而不是 component
  15. console.log(this.data.from)
  16. }
  17. });

通过例子可以发现,自定义组件的扩展其实就是提供了修改自定义组件定义段的能力。

使用扩展

Behavior() 构造器提供了新的定义段 definitionFilter,用于支持自定义组件扩展。 definitionFilter 是一个函数,在被调用时会注入两个参数:

  • 第一个参数是使用该 behavior 的 component/behavior 的定义对象;
  • 第二个参数是该 behavior 所使用的 behavior 的 definitionFilter 函数列表。

代码示例

在开发者工具中打开

在开发者工具中打开

在 WEB IDE 中打开

  1. // behavior3.js
  2. module.exports = Behavior({
  3. definitionFilter(defFields, definitionFilterArr) {}
  4. });
  5. // behavior2.js
  6. module.exports = Behavior({
  7. behaviors: [require('./behavior3.js')],
  8. definitionFilter(defFields, definitionFilterArr) {
  9. // definitionFilterArr[0](defFields)
  10. }
  11. });
  12. // behavior1.js
  13. module.exports = Behavior({
  14. behaviors: [require('./behavior2.js')],
  15. definitionFilter(defFields, definitionFilterArr) {}
  16. });
  17. // component.js
  18. Component({
  19. behaviors: [require('./behavior1.js')]
  20. });

说明

上述代码中声明了 1 个自定义组件和 3 个 behavior,每个 behavior 都使用了 definitionFilter 定义段。按照声明的顺序会有如下事情发生:

  • 当进行 behavior2 的声明时就会调用 behavior3 的 definitionFilter 函数,其中 defFields 参数是 behavior2 的定义段, definitionFilterArr 参数即为空数组,因为 behavior3 没有使用其他的 behavior 。
  • 当进行 behavior1 的声明时就会调用 behavior2 的 definitionFilter 函数,其中 defFields 参数是 behavior1 的定义段, definitionFilterArr 参数是一个长度为 1 的数组,definitionFilterArr[0] 即为 behavior3 的 definitionFilter 函数,因为 behavior2 使用了 behavior3。用户在此处可以自行决定在进行 behavior1 的声明时要不要调用 behavior3 的 definitionFilter 函数,如果需要调用,在此处补充代码 definitionFilterArr[0](defFields) 即可,definitionFilterArr 参数会由基础库补充传入。
  • 同理,在进行 component 的声明时就会调用 behavior1 的 definitionFilter 函数。

简单概括,definitionFilter 函数可以理解为当 A 使用了 B 时,A 声明就会调用 B 的 definitionFilter 函数并传入 A 的定义对象让 B 去过滤。此时如果 B 还使用了 C 和 D,那么 B 可以自行决定要不要调用 C 和 D 的 definitionFilter 函数去过滤 A 的定义对象。

真实案例

下面利用扩展简单实现自定义组件的计算属性功能:

代码示例

在开发者工具中打开

在开发者工具中打开

在 WEB IDE 中打开

  1. // behavior1.js
  2. module.exports = Behavior({
  3. lifetimes: {
  4. created() {
  5. // 原始 setData
  6. this._originalSetData = this.setData
  7. // 封装后的 setData
  8. this.setData = this.methods._setData
  9. }
  10. },
  11. definitionFilter(defFields) {
  12. const computed = defFields.computed || {}
  13. const computedKeys = Object.keys(computed)
  14. const computedCache = {}
  15. // 计算 computed
  16. const calcComputed = (scope, insertToData) => {
  17. const needUpdate = {}
  18. const data = defFields.data = defFields.data || {}
  19. for (let key of computedKeys) {
  20. // 计算新值
  21. const value = computed[key].call(scope)
  22. if (computedCache[key] !== value) needUpdate[key] = computedCache[key] = value
  23. // 直接插入到 data 中,初始化时才需要的操作
  24. if (insertToData) data[key] = needUpdate[key]
  25. }
  26. return needUpdate
  27. }
  28. // 重写 setData 方法
  29. defFields.methods = defFields.methods || {}
  30. defFields.methods._setData = function (data, callback) {
  31. // 原始 setData
  32. const originalSetData = this._originalSetData
  33. // 做 data 的 setData
  34. originalSetData.call(this, data, callback)
  35. // 计算 computed
  36. const needUpdate = calcComputed(this)
  37. // 做 computed 的 setData
  38. originalSetData.call(this, needUpdate)
  39. }
  40. // 初始化 computed
  41. // 计算 computed
  42. calcComputed(defFields, true)
  43. }
  44. })
  1. // 在组件js中
  2. const beh = require('./behavior1.js')
  3. Component({
  4. behaviors: [beh],
  5. data: {
  6. a: 0,
  7. },
  8. computed: {
  9. b() {
  10. return this.data.a + 100
  11. },
  12. },
  13. methods: {
  14. onTap() {
  15. this._originalSetData({
  16. a: ++this.data.a,
  17. })
  18. }
  19. }
  20. })
  1. <!-- 在组件swan中 -->
  2. <view>data: {{a}}</view>
  3. <view>computed: {{b}}</view>
  4. <button bindtap="onTap">click</button>