人人都会AI编程

Dep 与 Watcher 的对应关系

更新时间:2026-07-11

在 Vue 的响应式系统里,Dep(Dependency,依赖)和 Watcher(观察者)是两个关键角色,它们之间形成了一种“发布-订阅”的对应关系,共同完成了“数据变化时自动通知视图更新”的核心流程。

它们分别是什么

  • Dep:可以理解为“数据的通讯录”。每个被 Vue 转换成响应式的数据(如 data 里的一个属性),背后都有一个对应的 Dep 实例。这个通讯录里记录了“哪些地方依赖了我”——也就是哪些 Watcher 订阅了这个数据的变化。
  • Watcher:可以理解为“依赖数据的执行单元”。当你在模板里用了 {{ count }},或者写了一个 watch 回调,又或者定义了一个 computed 属性,Vue 内部都会创建一个对应的 Watcher。这个 Watcher 知道自己需要执行什么逻辑(更新 DOM、执行回调等),也知道自己依赖了哪些数据。

怎么建立关联:依赖收集

依赖收集的触发时机是在 Watcher 求值 的过程中。以 Vue 2 为例,流程如下:

  1. 当前 Watcher(比如组件的渲染 Watcher)开始工作,它把自己放到全局唯一的“当前目标”位置——Dep.target。
  2. 这个 Watcher 在执行时,会去 读取 模板里用到的响应式数据,比如 this.count。
  3. 读取 count 会触发它的 getter(由 Object.defineProperty 或 Proxy 拦截)。在 getter 内部,count 对应的 Dep 会执行 dep.depend()。
  4. dep.depend() 看到此时 Dep.target 有值,就把这个 Watcher 加入自己的订阅者列表(subs)。于是,这个 Dep 就记住了一个“我的变化要通知这个 Watcher”。
  5. 如果这个 Watcher 还读取了 this.name,那么 name 的 Dep 也会做同样的事,把当前 Watcher 加进去。

最终,一个 Watcher 可能订阅了多个 Dep,而一个 Dep 也可能被多个 Watcher 订阅——这是一种多对多的关系。渲染 Watcher 很可能订阅几十个 Dep,因为模板里用了几十个变量;而一个 Dep(比如全局用户信息)可能同时被渲染 Watcher、计算属性 Watcher、watch 回调 Watcher 共同订阅。

怎么触发更新:派发通知

当数据发生变化时,流程正好反过来:

  1. 你执行 this.count = 10,触发了 count 的 setter。
  2. setter 内部调用 dep.notify(),遍历自己通讯录(subs)里所有的 Watcher,逐个调用它们的 update() 方法。
  3. 每个收到通知的 Watcher 并不会立即执行,而是先把自己放进一个更新队列,等待批量处理(这就是第 7 章要讲的异步更新与 nextTick 原理)。
  4. 在合适的时机,队列里的 Watcher 会重新求值。对于渲染 Watcher,就是触发组件重新渲染,生成新的虚拟 DOM 树,再 diff 出最小更新量应用到真实 DOM。

Vue 3 中的概念演进

Vue 3 用 effect 取代了“Watcher”这个命名,用 track 和 trigger 取代了 depend 和 notify,但核心关系模型完全一致:

  • 你调用 ref() 或 reactive() 创建的数据,内部仍然维护着一个依赖集合(类似 Dep)。
  • 当 effect(或 computed、watchEffect)执行时,其副作用函数被包裹,执行中读取响应式数据会触发 track,将此 effect 与数据关联。
  • 数据变化时调用 trigger,通知所有关联的 effect 重新运行。

一张图简单总结

[响应式数据 count]  ── owns ──▶  Dep (subs: [Watcher A, Watcher B])
                                        │
                        ┌───────────────┼───────────────┐
                        ▼                               ▼
                Watcher A (渲染组件)              Watcher B (watch 回调)
                  依赖: count, name                  依赖: count

这种一对多的订阅模型让 Vue 实现了精确更新——count 改变时,只有真正用到它的 Watcher 才会被通知,而不会波及到无关区域。这也是响应式系统高效运转的基石。