watcher的运行路径就是: 开始 -> ParentWatcher -> SonWatcher -> ParentWatcher -> 结束。
是不是特别像函数运行中的入栈出栈,没错,Vue内部就是用了栈的数据结构来记录watcher的运行轨迹。
// watcher栈
const targetStack = []// 将上一个watcher推到栈里,更新Dep.target为传入的_target变量。
export function pushTarget(_target) {
if (Dep.target) targetStack.push(Dep.target)
Dep.target = _target
}
// 取回上一个watcher作为Dep.target,并且栈里要弹出上一个watcher。
export function popTarget() {
Dep.target = targetStack.pop()
}
有了这些辅助的工具,就可以来看看Watcher的具体实现了
import Dep, { pushTarget, popTarget } from './dep'export default class Watcher {
constructor(getter) {
this.getter = getter
this.get()
}
get() {
pushTarget(this)
this.value = this.getter()
popTarget()
return this.value
}
update() {
this.get()
}
}
回顾一下开头示例中Watcher的使用。
const data = reactive({
msg: 'Hello World',
})new Watcher(() => {
document.getElementById('app').innerHTML = `msg is ${data.msg}`
})
传入的getter函数就是
() => {
document.getElementById('app').innerHTML = `msg is ${data.msg}`
}
在构造函数中,记录下getter函数,并且执行了一遍get
get() {
pushTarget(this)
this.value = this.getter()
popTarget()
return this.value
}
在这个函数中,this就是这个watcher实例,在执行get的开头先把这个存储了渲染函数的watcher设置为当前的Dep.target,然后执行this.getter()也就是渲染函数
在执行渲染函数的途中读取到了data.msg,就触发了defineReactive函数中劫持的get:
Object.defineProperty(data, key, {
get() {
dep.depend()
return val
}
})
这时候的dep.depend函数:
depend() {
if (Dep.target) {
this.deps.add(Dep.target)
}
}
所收集到的Dep.target,就是在get函数开头中pushTarget(this)所收集的
new Watcher(() => {
document.getElementById('app').innerHTML = `msg is ${data.msg}`
})
这个watcher实例了。
此时我们假如执行了这样一段赋值代码:
data.msg = 'ssh'










