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

Vue 3 中的概念演进

Vue 3 用 effect 取代了“Watcher”这个命名,用 tracktrigger 取代了 dependnotify,但核心关系模型完全一致:

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

一张图简单总结

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

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