参考资料:
https://blog.csdn.net/qq_37939251/article/details/90683466
https://blog.csdn.net/afk46847/article/details/101509059(主要是这篇)
依赖收集(源码级理解)
当创建watcher的时候,会触发watcher.get方法
watcher.get方法如下
get () {
pushTarget(this)
let value
const vm = this.vm
try {
value = this.getter.call(vm, vm)
} catch (e) {
// 省略...
} finally {
// 省略...
popTarget()
this.cleanupDeps()
}
return value
}
get方法中会去调用watcher.getter方法【value = this.getter.call(vm, vm)】
对于computedWatcher来说是computed中属性的表达式
对于renderWatcher来说watcher.getter方法是updateComponent,
这个方法会生成renderFunction,然后将renderFunction生成vnode
生成vnode的过程中会获取data中属性的值,进而会触发data中数据的getter方法,getter方法会触发依赖收集
dep的depend方法如下:(Dep.target是当前的watcher)
depend () {
if (Dep.target) {
Dep.target.addDep(this)
}
}
watcher的addDep方法如下
addDep (dep: Dep) {
const id = dep.id
if (!this.newDepIds.has(id)) {
this.newDepIds.add(id)
this.newDeps.push(dep)
if (!this.depIds.has(id)) {
dep.addSub(this)
}
}
}
addDep方法将当前watcher中没有收集的dep收集到当前watcher中【进行了去重】,
因为dep中会保留上一次依赖的watcher,将上一次没有的watcher收集到dep中
watcher.cleanupDeps()方法用于设置deps和newDeps的状态
例子
<div>{{a}} {{b}}</div> 对应一个renderWatcher
data: {
a: 1, -> 对应dep(a)
b: 2 -> 对应dep(b)
}
第一次页面初始化后,首先是
renderWatcher dep(a) dep(b)
deps newDeps
[] [dep(a), dep(b)] [renderWatcher] [renderWatcher]
然后执行了watcher.clearnupDeps
[dep(a), dep(b)] [] [renderWatcher] [renderWatcher]
当修改b的值时候
[[dep(a), dep(b)] [dep(b)] [renderWatcher] [renderWatcher]
执行了watcher.clearnupDeps之后
[dep(b)] [] [renderWatcher] [renderWatcher]