在Vue的响应式系统中,dep.addSub(this)是实现Dep→Watcher关联的关键代码,而组件销毁时的依赖清理则通过Watcher的teardown机制实现,下面主要介绍下两者的联系。
双向绑定的整体实现逻辑
- Watcher侧:通过
newDeps和newDepIds记录当前Watcher依赖的所有Dep实例 - Dep侧:通过
addSub将Watcher存入subs数组,形成反向引用这种双向记录确保: - 数据变更时能精准找到关联Watcher(Dep→Watcher)
- 组件销毁时可清理无用依赖(Watcher→Dep)
Dep类中的具体源码:
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)
}
}
}
一、Dep→Watcher 精准通知机制
-
dep.addSub(this)的核心作用
当Watcher在求值过程中访问响应式数据时,会通过dep.depend()触发依赖收集,最终调用watcher.addDep()方法。在该方法中,若发现是新依赖(通过depIds判断),则会执行dep.addSub(this)将当前Watcher实例添加到Dep的subs数组
// Watcher.addDep 方法片段
if (!this.depIds.has(id)) {
dep.addSub(this) // 建立Dep到Watcher的引用
}
2.数据变更时的通知流程
当数据变更触发setter时,会调用dep.notify()遍历subs数组中的所有Watcher,依次执行其update()方法,实现精准更新
二、Watcher→Dep 依赖清理机制
- 组件销毁时的清理入口
组件销毁时会调用Watcher的teardown()方法(或stop()),该方法会遍历Watcher的所有依赖(deps数组),调用dep.removeSub(this)将自身从Dep的订阅列表中移除
teardown() {
if (this.vm && !this.vm._isBeingDestroyed) {
remove(this.vm._scope.effects, this)
}
if (this.active) {
let i = this.deps.length
while (i--) {
this.deps[i].removeSub(this)//// 从Dep中移除当前Watcher
}
this.active = false// 标记为无效状态
if (this.onStop) {
this.onStop()
}
}
}
这一过程确保:
- 被销毁组件的Watcher不再接收无用通知
- Dep的
subs数组保持最小化,避免内存泄漏