为了更好定制自定义组件的功能,可以使用自定义组件扩展机制。
扩展后的效果
为了更好地理解扩展后的效果,先举一个例子:
// 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); // 此处会发现输出 behavior 而不是 component},
});通过例子可以发现,自定义组件的扩展其实就是提供了修改自定义组件定义段的能力。上述例子就是修改了自定义组件中的 data 定义段里的内容。
使用扩展
Behavior() 构造器提供了新的定义段 definitionFilter,用于支持自定义组件扩展。definitionFilter 是一个函数,在被调用时会注入两个参数。第一个参数是使用该 behavior 的 component/behavior 的定义对象,第二个参数是该 behavior 所使用的 behavior 的 definitionFilter 函数列表。
以下举个例子来说明:
// 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],
});上述代码中声明了 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 的定义对象。
真实案例
下面利用扩展简单实现自定义组件的计算属性功能。
// behavior.js
export default Behavior({lifetimes: {created() {this._originalSetData = this.setData; // 原始 setDatathis.setData = this._setData; // 封装后的 setData},},definitionFilter(defFields) {const computed = defFields.computed || {};const computedKeys = Object.keys(computed);const computedCache = {};// 计算 computedconst calcComputed = (scope, insertToData) => {const needUpdate = {};const data = (defFields.data = defFields.data || {});for (let key of computedKeys) {const value = computed[key].call(scope); // 计算新值if (computedCache[key] !== value)needUpdate[key] = computedCache[key] = value;if (insertToData) data[key] = needUpdate[key]; // 直接插入到 data 中,初始化时才需要的操作}return needUpdate;};// 重写 setData 方法defFields.methods = defFields.methods || {};defFields.methods._setData = function (data, callback) {const originalSetData = this._originalSetData; // 原始 setDataoriginalSetData.call(this, data, callback); // 做 data 的 setDataconst needUpdate = calcComputed(this); // 计算 computedoriginalSetData.call(this, needUpdate); // 做 computed 的 setData};// 初始化 computedcalcComputed(defFields, true); // 计算 computed},
});在组件中使用:
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,});},},
});<view>data: {{a}}</view>
<view>computed: {{b}}</view>
<button bind:tap="onTap">click</button>实现原理很简单,对已有的 setData 进行二次封装。在每次 setData 的时候计算出 computed 里各字段的值,然后设到 data 中,以达到计算属性的效果。
此实现只是作为一个简单案例来展示,请勿直接在生产环境中使用。