Post-extension impact
// 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) // Here, one would observe that the output is 'behavior' rather than 'component'.
}
})
Through the given example, it becomes evident that the extension of custom components essentially provides the capability to modify the definition section of these components. The aforementioned illustration demonstrates the modification of content within the data
definition section of the custom component.
Use extension
The Behavior() constructor introduces a new definition section,definitionFilter, to facilitate the extension of custom components. definitionFilter is a function that, when invoked, injects two parameters. The first parameter is the definition object of the component/behavior employing this behavior, while the second parameter is the list of definitionFilter functions used by this behavior.
Let's illustrate with an example:
// 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')],
})
In the aforementioned code, one custom component and three behaviors are declared, each employing the definitionFilter
definition section. Following the order of declaration, the ensuing events would occur:
1. Upon the declaration of behavior2, the definitionFilter
function of behavior3 is invoked. Here, the defFields
parameter pertains to the definition section of behavior2, and the definitionFilterArr
parameter is an empty array, given that behavior3 does not employ any other behavior.
2. Upon the declaration of behavior1, the definitionFilter function of behavior2 is invoked. Here, the defFields parameter pertains to the definition section of behavior1, and the definitionFilterArr
parameter is an array with a length of 1, where definitionFilterArr[0]
is the definitionFilter
function of behavior3, given that behavior2 employs behavior3. Users can decide whether to invoke the definitionFilter
function of behavior3 during the declaration of behavior1. If invocation is required, supplementing the code with definitionFilterArr[0](defFields)
will suffice, and the definitionFilterArr
parameter will be supplemented and passed in by the base library.
Similarly, upon the declaration of the component, the definitionFilter
function of behavior1 is invoked.
In summary, the definitionFilter
function can be understood as follows: when A employs B, the declaration of A invokes the definitionFilter
function of B, passing in the definition object of A for B to filter. If B also employs C and D, B can independently decide whether to invoke the definitionFilter
functions of C and D to filter the definition object of A.
Real-world Scenario
The following demonstrates the implementation of a custom component's computed attribute functionality using 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 = {}
// Compute "computed"
const calcComputed = (scope, insertToData) => {
const needUpdate = {}
const data = defFields.data = defFields.data || {}
for (const key of computedKeys) {
const value = computed[key].call(scope) // Compute new value
if (computedCache[key] !== value) needUpdate[key] = computedCache[key] = value
if (insertToData) data[key] = needUpdate[key] // Direct insertion into data, an operation required only during initialization
}
return needUpdate
}
// Overwrite 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 on data
const needUpdate = calcComputed(this) // Compute "computed"
originalSetData.call(this, needUpdate) // Perform setData on "computed"
}
// Initialize "computed"
calcComputed(defFields, true) // Compute "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 straightforward. It involves a secondary encapsulation of the existing setData. Each time setData is invoked, the values of each field in computed are calculated and then set in data, thereby computing the attributes.
Note:
This implementation example is merely illustrative and should not be directly employed in a production environment.
Official Extension Package
Was this page helpful?