watch一个Computed属性:
其实了解完前面的依赖收集原理之后,watch一个computed属性和data属性并没有什么区别,就是代理computed属性到vm实例上去,那么在依赖收集的时候computed中包含的属性也会被收集到,代码如下:
// 初始化computed
function initComputed (vm) {
var computed = vm._computed = vm.$options.computed
var keys = Object.keys(computed)
var i = keys.length
while (i--) {
proxyComputed(vm, keys[i])
}
}
// 代理computed属性到vm实例上
function proxyComputed (vm, key) {
Object.defineProperty(vm, key, {
configurable: true,
enumerable: true,
get: function () {
return vm._computed[key].call(vm)
}
})
}
执行代码:
var app = new Vue({
data: function () {
return {
a: 1,
b: {
c: 2
},
arr: [1,2,3]
}
},
computed: {
computed1: function () {
return this.a + this.b.c
}
}
})
app.$watch('a', function () {
console.log('a is change')
})
app.$watch('computed1', function () {
console.log('computed1 is change')
})
app.a = 2
输出结果:
a is change
computed1 is change
完美!但是一看vue的源码,实现起来似乎复杂好多,那么vue为什么要那样做呢,看来事情并不简单(托了下眼镜)。
其实vue的源码中,Watcher的options中有一个lazy的参数,这个参数主要就是用在computed中的。(相关源码)
lazy watcher主要就是实现了vue中computed的缓存,我们都知道,computed计算属性的结果会被缓存,除非依赖的响应式属性变化才会重新计算。这就是靠lazy watcher实现的。
我就试着用自己的语言说说vue中的lazy watcher的思路吧。
1.为每个定义的computed属性创建一个内部的监视器(lazy Watcher实例),保存在vm._computedWatchers中,lazy Watcher的dirty属性为默认true,数据为脏,因为定义时不去计算watcher的值,首次访问时才会去计算并收集此watcher到computed相关属性的dep中。
2.代理computed属性到vm实例,在代理属性的getter中,若数据为脏,则调用lazyWatcher的evaluate计算最新的数据,并设置dirty为false。若数据不为脏,则直接返回lazyWatcher的value。
3.computed相关的属性更新时候,会触发执行lazyWatcher的update方法;若是lazyWatcher,则在update方法中只会把watcher的dirty设置为true,等下次引用该watcher的时候就会按上述步骤重新计算值
网友评论