vue中watcher可以分为三类,render watcher, users watcher, lazy watcher
计算属性其实就是lazy watcher
lazy watcher
initComputed
对computed对象内的键值进行初始化处理,如果键值在data/prop内出现过就忽略,如果没有出现过就对其创建相应的watcher,并且设置lazy为true。
最后通过defineComputed方法键值挂载在vm实例上
defineComputed
这里注意的是对于SSR时,每次计算属性的值都会重新计算一遍,而非SSR时候,值是由缓存的。这里主要是用了createComputedGetter方法
createComputedGetter
将计算属性的get赋值给computedGetter,而这个函数返回的是watcher.value。
上面提到了缓存计算结果。那么知道了如何得到watcher.value就知道了计算属性的缓存策略。
并且将watcher.deps内的说有dep与渲染watcher依赖,保证计算属性变更后视图也进行更新
计算属性的缓存策略/watcher.value取值
实例化lazy watcher
为computed对象的每一个key(该key不在data/prop中)创建一个watcher
其初始化配置如下
1. watcher.vm=vm vm为key所在的组件实例
2. watcher.getter为计算属性每个key对应的function值或者get属性的值。也就是说watcher.getter比为一个function
3. watcher.cb = noop
4. watcher.options = { lazy:true }
5. watcher.lazy = watcher.dirty = true
6. watcher.value = undefined
获取计算属性的值
通过调用属性的get方法即computedGetter方法来取值。
其中有几个判断:
1. watcher.dirty
在初次取值的时候dirty为true,然后就调用watcher的evalute方法,进行取值,并将dirty改为false
get方法主要是调用了用户定义的function来后去计算属性的值,同时进行一个依赖收集,获取计算属性的依赖
在依赖的属性有进行更新的时候,会触发当前lazywatcher的update方法,将dirty设置为true,也就说下次取值的
这里仅将dirty设置成true
dirty 脏值
举个例子
computed:{
get(){
return this.a + this.b
}
set(){
}
}
个人理解这个计算属性就是一个处理“脏值”的过程。
1. vue一开始默认初始化的计算属性是一个脏值,所有在第一次求值后,获取到了最新的值,那么计算属性也就不脏了,就把dirty设置为false。
2. 在第一次求值的时候,调用了wathcer.get()方法,此时将wathcer 压栈,取this.a和this.b的值的时候,因为vue hack了对象的get方法,因此会进行一次依赖收集,也就是当前wathcer即lazy watcher会收集a,b两个属性的依赖
3. 当a, b两个属性其中一个值发生了改变的时候会将a, b对应的dep实例中收集的watcher拿出来更新,此时触发了lazy watcher。
4. 依赖的属性发生变化,触发lazy watcher这一行为导致计算属性的数组“变脏“了,那么就给重新标记watcher.dirty = true,
5. 当再一次获取值的时候就要重写执行一次用户自定义的取值计算,并且将dirty设置为false
6. 除了lazy watcher意外渲染watcher也会随后进行更新
总结
通过“脏值”这一概念,加深了对lazy的理解。lazy是分为两层的,第一层是首次的默认值是undefined,只有取值的时候才进行计算;第二层是只有二次计算的结果不一样才会去更新watcher.value