---
title: Custom Component Extension
---


To better customize the functionality of custom components, you can use the custom component extension mechanism.

## Effect of extension

Here is an example to help you get to know the extension effect.

```js
// behavior.js
export default Behavior({
  definitionFilter(defFields) {
    defFields.data.from = 'behavior';
  },
});

// component.js
import myBehavior from 'behavior.js';
Component({
  data: {
    from: 'component',
  },
  behaviors: [myBehavior],
  ready() {
    console.log(this.data.from); // You can see that the output here is behavior, instead of component
  },
});
```

In this example, extensions to the custom component enable you to modify the data definition field in the custom component. In the above example, the `data` definition field in the custom component is modified.

## How to use extensions

The `Behavior()` constructor provides a new definition field `definitionFilter` to support custom component extensions. When requested, the `definitionFilter` function is injected with two parameters: the `component/behavior` definition object that uses this `behavior`, and the `definitionFilter` function list of the `behavior` used by this `behavior`.

Here is an example:

```js
// behavior3.js
export default Behavior({
  definitionFilter(defFields, definitionFilterArr) {},
});

// behavior2.js
import behavior3 from 'behavior3.js';
export default Behavior({
  behaviors: [behavior3],
  definitionFilter(defFields, definitionFilterArr) {
    // definitionFilterArr[0](defFields)
  },
});

// behavior1.js
import behavior2 from 'behavior2.js';
export default Behavior({
  behaviors: [behavior2],
  definitionFilter(defFields, definitionFilterArr) {},
});

// component.js
import behavior1 from 'behavior1.js';
Component({
  behaviors: [behavior1],
});
```

The above code block declares one custom component and three behaviors. Each `behavior` uses the `definitionFilter` definition field. And then, the following events will occur in the order of declaration:

1. When the `behavior2` declaration is made, the `definitionFilter` function of `behavior3` is called. The `defFields` parameter is the definition field of `behavior2`, and the `definitionFilterArr` parameter is an empty array, because `behavior3` does not use another `behavior`.

2. When the `behavior1` declaration is made, the `definitionFilter` function of `behavior2` is called. The `defFields` parameter is the definition field of `behavior1`, the `definitionFilterArr` parameter is an array with a length of 1, and `definitionFilterArr[0]` is the `definitionFilter` function of `behavior3`, because `behavior2` uses `behavior3`. You can decide whether to call the `definitionFilter` function of `behavior3` when making the declaration of `behavior1`. If the call is needed, add the code `definitionFilterArr[0](defFields)` here. The `definitionFilterArr` parameter will be passed in by the base library.

3. Similarly, when the `component` is declared, the `definitionFilter` function of `behavior1` is called.
   To be brief, the `definitionFilter` function means that when A uses B, the A declaration will call B's `definitionFilter` function and pass in A's definition object for B to filter data. If B also uses C and D, then, B can decide whether to call the `definitionFilter` function of C and D to filter A's definition object.

## Cases

The following code block uses the extension to simply implement the computed property feature of a custom component.

```js
// behavior.js
export default Behavior({
  lifetimes: {
    created() {
      this._originalSetData = this.setData; // Original setData
      this.setData = this._setData; // Packaged setData
    },
  },
  definitionFilter(defFields) {
    const computed = defFields.computed || {};
    const computedKeys = Object.keys(computed);
    const computedCache = {};

    // Calculate field values of 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 a new value
        if (computedCache[key] !== value)
          needUpdate[key] = computedCache[key] = value;
        if (insertToData) data[key] = needUpdate[key]; // Insert the value directly into data. This operation is required only during initialization.
      }

      return needUpdate;
    };

    // Rewrite the setData method
    defFields.methods = defFields.methods || {};
    defFields.methods._setData = function (data, callback) {
      const originalSetData = this._originalSetData; // Original setData
      originalSetData.call(this, data, callback); // Perform setData for data
      const needUpdate = calcComputed(this); // Calculate the value of computed
      originalSetData.call(this, needUpdate); // Perform setData for computed
    };

    // Initialize computed
    calcComputed(defFields, true); // Calculate computed
  },
});
```

### Usage in a component:

```js
import beh from './behavior.js';
Component({
  behaviors: [beh],
  data: {
    a: 0,
  },
  computed: {
    b() {
      return this.data.a + 100;
    },
  },
  methods: {
    onTap() {
      this.setData({
        a: ++this.data.a,
      });
    },
  },
});
```

```xml
<view>data: {{a}}</view>
<view>computed: {{b}}</view>
<button bind:tap="onTap">click</button>
```

The implementation principle is very simple, and the existing `setData` can be packaged in a custom way. Calculate the value of each field in `computed` every time you perform `setData`, and then set it in to `data` to implement computed properties as required.

> This example is only for your reference. Do not use it directly in your production environment.
