我们通过下面的例子来看下 computed 属性的实现机制。为了简单起见,我们创建一个没有挂载节点的Vue实例。
var example = new Vue({
data: {
a: 1
},
computed: {
b: function () {
return this.a + 1
}
}
})
example.a++
console.log(example.b) // -> 3
example.a = 5
console.log(example.b) // -> 6
data属性
computed属性中的方法和data属性直接相关,data对象的字段都会经过如下方法实现响应式:
function defineReactive (obj, key, val) {
var dep = new Dep() // dep对象负责维护订阅列表(对数据而言,就是Watcher对象)
// cater for pre-defined getter/setters
var getter, setter
if (config.convertAllProperties) {
var property = Object.getOwnPropertyDescriptor(obj, key)
if (property && property.configurable === false) {
return
}
getter = property && property.get
setter = property && property.set
}
var childOb = observe(val)
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter () {
var value = getter ? getter.call(obj) : val
if (Dep.target) { // Dep.target是个Watcher对象实例
dep.depend() // 把Dep.target这个Watcher对象加入到dep的subs列表中
if (childOb) {
childOb.dep.depend()
}
if (isArray(value)) {
for (var e, i = 0, l = value.length; i < l; i++) {
e = value[i]
e && e.__ob__ && e.__ob__.dep.depend()
}
}
}
return value
},
set: function reactiveSetter (newVal) {
var value = getter ? getter.call(obj) : val
if (newVal === value) {
return
}
if (setter) {
setter.call(obj, newVal)
} else {
val = newVal
}
childOb = observe(newVal)
// 此处会调用依赖列表的watcher进行数据视图的同步!
// watcher会调用directive的更新方法
dep.notify()
}
})
}
computed属性
初始化时,computed属性的主要方法如下:
Vue.prototype._initComputed = function () {
var computed = this.$options.computed
if (computed) {
for (var key in computed) {
var userDef = computed[key]
var def = {
enumerable: true,
configurable: true
}
if (typeof userDef === 'function') {
def.get = makeComputedGetter(userDef, this)
def.set = noop
} else {
def.get = userDef.get
? userDef.cache !== false
? makeComputedGetter(userDef.get, this)
: bind(userDef.get, this)
: noop
def.set = userDef.set
? bind(userDef.set, this)
: noop
}
Object.defineProperty(this, key, def)
}
}
}
function makeComputedGetter (getter, owner) {
var watcher = new Watcher(owner, getter, null, {
lazy: true
})
return function computedGetter () {
if (watcher.dirty) {
watcher.evaluate()
}
if (Dep.target) {
watcher.depend()
}
return watcher.value
}
}
关联
第一次获取computed属性中的字段的值时,会因为watcher的lazy为true,执行一次evaluate方法,而且会把watcher加入到data字段的订阅者列表中。
DOM节点中有和computed属性相关的字段,会在调用get方法时,通过computed中的getter方法完字段取值、依赖搜集。
如果对computed中的字段直接设值,并不能引起View的变化。
网友评论