Vue.js 2.x 的响应式核心依赖 Object.defineProperty 劫持属性,并通过 Dep 类实现依赖收集与通知更新;Dep 为每个响应式属性提供独立订阅通道,在 getter 中收集 Watcher、setter 中通知更新,支持去重与清理,但存在无法监听动态操作等局限,Vue 3 改用 Proxy 重构。

Vue.js 2.x 的响应式核心依赖于 Object.defineProperty 对数据属性进行劫持,并通过 Dep(Dependency)类 实现依赖收集与通知更新。Dep 类本身不直接存储 watcher,而是作为“依赖管理中枢”,为每个响应式属性(或 computed、watch 等)提供独立的订阅通道。
Dep 类的核心职责
Dep 是一个简单的发布-订阅容器,每个被 defineProperty 劫持的响应式属性都拥有一个专属 Dep 实例。它的主要作用是:
- 在 getter 中收集当前正在求值的 Watcher(即“谁在读这个属性”)
- 在 setter 中通知所有已收集的 Watcher 执行更新(即“谁需要重新计算”)
- 避免重复添加同一 Watcher,支持依赖清理(如组件卸载时)
Dep 类的基本结构与关键方法
简化版实现如下(贴近 Vue 2 源码逻辑):
<!-- 伪代码示意 -->
立即学习“前端免费学习笔记(深入)”;
递归分析 Vue 项目组件依赖,从入口文件生成组件层级图,支持 Vue 2/3,输出组件名、文件路径和属性。适用于分析组件结构、排查依赖或了解项目架构。
class Dep {
constructor() {
this.id = uid++ // 全局唯一标识,用于去重和调试
this.subs = [] // 存储所有依赖该属性的 Watcher 实例
}
<p>// 添加一个 watcher 到订阅列表(带去重)
addSub(sub) {
if (sub && !this.subs.includes(sub)) {
this.subs.push(sub)
}
}</p><p>// 移除一个 watcher(例如组件销毁时调用)
removeSub(sub) {
const index = this.subs.indexOf(sub)
if (index > -1) {
this.subs.splice(index, 1)
}
}</p><p>// 通知所有 watcher 更新(触发 update 方法)
notify() {
const subs = this.subs.slice() // 避免遍历时数组被修改
for (let i = 0; i < subs.length; i++) {
subs[i].update()
}
}
}</p><p>// 全局静态属性:当前正在求值的 watcher(由 pushTarget / popTarget 控制)
Dep.target = null
注意:Dep.target 是关键桥梁——它在依赖收集阶段临时保存当前活跃的 Watcher,使得 getter 能知道“该把谁加进 subs”。
Dep 如何与 defineProperty 和 Watcher 协同工作
整个流程依赖三者配合:
-
defineProperty 的 getter:当访问响应式属性时触发,内部调用
dep.depend()(即dep.addSub(Dep.target)),将当前 Watcher 记录下来 - Watcher 构造时:会主动触发一次 get,使自身成为 Dep.target,从而完成初次依赖收集
-
defineProperty 的 setter:触发
dep.notify(),逐个唤醒 subs 中的 Watcher 执行异步更新(如 queueWatcher)
例如:data: { count: 1 },模板中用了 {{ count }},渲染时访问 count 触发 getter → 收集渲染 Watcher 到该属性对应的 Dep → 后续 count = 2 触发 setter → 通知该 Dep 下所有 Watcher 重新渲染。
Dep 的局限与 Vue 3 的演进
Dep 在 Vue 2 中存在一些限制:
- 无法监听对象新增/删除属性(需
Vue.set)、数组索引赋值、length 修改等 - 依赖关系是静态建立的,对动态 key 或 Proxy 代理不友好
- 每个属性一个 Dep,深层嵌套对象会创建大量 Dep 实例,内存开销较大
Vue 3 改用 Proxy + WeakMap + effect 函数重构响应式系统,Dep 的角色被更灵活的 track / trigger 机制替代,不再显式暴露 Dep 类,但思想一脉相承:仍是“读时收集、写时通知”的响应式范式。

















