我们上次分析vue源码讲的是收集依赖,数据变化之后我们把依赖收集到dep类中,通过这个管理器进行管理。
里面有一个subs数组,用来存放依赖,并且定义了几个实例方法用来依赖进行添加,删除,通过操作。
比如addSub,removeSub,depend、notify等方法。
依赖到底是谁
我们dep是用来管理依赖的,那么要添加的依赖到底是谁?
其实这个依赖就是我们常说的watcher的类,也就是说谁用了数据,谁就是那个依赖。
谁用到了数据,我们就为谁创建一个Watcher实例。在之后数据变化的时候,我们不去直接通知依赖更新,而是通知依赖对应的Watch实例,由Watcher实例去通知真正的视图。
export default class Watcher implements DepTarget {
}
这个就是Watcher类,实现了DepTarget。
而DepTarget这个接口在dep类里面实现的。
export interface DepTarget extends DebuggerOptions {
id: number
addDep(dep: Dep): void
update(): void
}
Wathcer构造函数
下面是Watcher构造函数的源码。
constructor(
vm: Component | null,
expOrFn: string | (() => any),
cb: Function,
options?: WatcherOptions | null,
isRenderWatcher?: boolean
) {
// 初始化代码
// 此处省略部分代码...
this.value = this.lazy ? undefined : this.get()
}
这个就是在创建Watcher实例的时候初始执行的部分。在创建Watcher实例的时候会自动把自己添加到这个数据的依赖管理器中。
在最后一行有个this.value,执行了this.get();
这个get函数源码如下
get() {
pushTarget(this)
let value
const vm = this.vm
try {
value = this.getter.call(vm, vm)
} catch (e: any) {
if (this.user) {
handleError(e, vm, `getter for watcher "${this.expression}"`)
} else {
throw e
}
} finally {
// "touch" every property so they are all tracked as
// dependencies for deep watching
if (this.deep) {
traverse(value)
}
popTarget()
this.cleanupDeps()
}
return value
}
这里get函数第一步就是把这个watcher实例给了Dep.target。
然后在通过this.getter.call(vm, vm)获取一下被依赖的数据,这里的getter就是
更新组件重新传染函数updateComponent。
当数据发生变化的时候,会触发set函数,set函数接着会触发dep.nofity函数。然后执行Watcher类中的update()方法,调用数据变化的更新回调函数,从而更新视图。