# Custom Component Extensions

To better customize the functionality of a custom component, you can use the custom component extension mechanism. Mini Program base library version 2.2.3 Start supporting.

# Effect of Extended

To better understand the effect of the extension, let's take an example:

{% Minicode ('STePQRmH7Q5H') %}

// 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) // You'll find the output here. behavior Instead of component
  }
})

Through the example, it can be found that the extension of custom components is actually providing the ability to modify the custom component definition segment, the above example is to modify the custom component in the Data Define what's in the paragraph.

# Using Extensions

Behavior() The constructor provides a new definition segment definitionFilter , is used to support custom component extensions. definitionFilter Is a function that injects two arguments when it is called. The first argument is to use the behavior of component/behavior The second argument is the definition object of the behavior Used by behavior of definitionFilter List of functions.

Here's an example to illustrate:

// 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')],
})

The above code declares 1 custom component and 3 Behavior, each behavior All used definitionFilter Definition paragraph. In the order of declarations, the following things will happen:

  1. When carrying out behavior2 Is called when the declaration of the behavior3 of definitionFilter Function, in which defFields The parameters are behavior2 Of the definition paragraph, definitionFilterArr The argument is an empty array because behavior3 Did not use other behavior 。
  2. When carrying out behavior1 Is called when the declaration of the behavior2 of definitionFilter Function, in which defFields The parameters are behavior1 Of the definition paragraph, definitionFilterArr Argument is an array of length 1,definitionFilterArr[0] to be considered to be behavior3 of definitionFilter Function, because the behavior2 Used the behavior3。The user can decide at his own discretion. behavior1 You want to call the behavior3 of definitionFilter Function, if you need to call it, add the code here definitionFilterArr[0](defFields) Can be,definitionFilterArr Parameters are fed in by the base library.
  3. Similarly, in carrying out component Is called when the declaration of the behavior1 of definitionFilter Function.

In a nutshell,definitionFilter Function can be understood as a function when A Used the B When, A The declaration calls the B of definitionFilter Function and pass in the A The definition object lets B To filter. At this time if B Also used C and D , Then B You can decide for yourself whether to call C and D of definitionFilter Function to filter A The definition object of the.

Code example:

{% Minicode ('WaqPbxmN7E1j') %}

# Real cases

The following is a simple implementation of the Compute Properties feature for custom components with extensions:

// behavior.js
module.exports = Behavior({
  lifetimes: {
    created() {
      this._originalSetData = this.setData // Original setData
      this.setData = this._setData // Encapsulated setData
    }
  },
  definitionFilter(defFields) {
    const computed = defFields.computed || {}
    const computedKeys = Object.keys(computed)
    const computedCache  = {}

    // Calculation computed
    const calcComputed = (scope, InsertToData ) => {
      const needUpdate = {}
      const Data = defFields.data = defFields.data || {}

      for (let key of computedKeys) {
        const value = computed[key].call(scope) // Calculate the new value
        if (computedCache [key] !== value) needUpdate[key] = computedCache [key] = value
        if (InsertToData ) Data[key] = needUpdate[key] // Insert directly into the Data Operations that are only needed at initialization
      }

      return needUpdate
    }

    // Rewrite setData method
    defFields.methods = defFields.methods || {}
    defFields.methods._setData = function (data, callback) {
      const originalSetData = this._originalSetData // Original setData
      originalSetData.call(this, data, callback) // do Data of setData
      const needUpdate = calcComputed(this) // Calculation computed
      originalSetData.call(this, needUpdate) // do computed of setData
    }

    // to initialize computed
    calcComputed(defFields, true) // Calculation computed
  }
})

Use in components:

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: {{b}}</view>
<button bindtap="onTap" >click</button>

The implementation principle is simple, for existing setData For secondary encapsulation, in each setData Time to calculate. computed The value of each field, and then set to Data In order to achieve the effect of calculating properties.

This implementation is shown as a simple example and should not be used directly in a production environment.

# Official Expansion Pack

  • [computed](https://github.com/WeChat mini-program /computed)