Vue2响应式依赖收集本质是数据通过Dep实例记住使用它的Watcher,由Observer劫持属性、Dep管理订阅者、Watcher定义响应逻辑,三者协同构成发布-订阅闭环。

Vue2 的响应式依赖收集,本质是让数据知道“谁在用我”,而发布订阅模式就是这套通知机制的骨架。它不是抽象概念,而是由 Observer、Dep 和 Watcher 三个角色协同完成的具体流程。
依赖收集:数据怎么记住谁用了它?
每个响应式属性(比如 data.message)背后都绑着一个独立的 Dep 实例。当组件渲染时,会执行模板对应的 render 函数——这个过程会读取 message,触发它的 get 方法。
此时关键一步发生:
-
Dep.target被设为当前正在运行的Watcher(即该组件的渲染 watcher) -
dep.depend()被调用,把当前Watcher加入这个Dep的subs数组 -
Watcher同时也会记录自己依赖了这个Dep,形成双向关联
也就是说,不是“组件记住了数据”,而是“数据记住了组件”。一个属性可以被多个组件、计算属性或 watch 共同使用,它的 Dep 就会存多个 Watcher。
发布订阅:数据变化后怎么精准通知?
当给 message 赋新值时,触发 set 方法,内部调用 dep.notify():
- 遍历
dep.subs中所有Watcher - 对每个
Watcher执行update() -
update()通常会把更新任务推入异步队列,最终触发组件重新渲染或计算属性重新求值
这个过程完全解耦:数据不关心谁在用它,只管发通知;Watcher 不主动轮询,只等通知到来再响应。这就是典型的发布-订阅模式——Dep 是发布者,Watcher 是订阅者。
将静态 Vue 页面(index.vue)改造为动态页面(Custom.vue);稿源仅限 index.vue、其引用的 CSS 与资源路径,禁止参考仓库内其他文件。Invoke when user wants to transform a static Vue page into a dynamic data-driven page with Element UI components.
为什么必须用 Dep + Watcher 配合?
单靠 Object.defineProperty 只能拦截读写,无法自动建立“谁读了谁”这个映射关系。而 Dep 提供了统一的依赖池,Watcher 提供了上下文和回调逻辑:
-
Dep管理“有哪些订阅者”,负责广播 -
Watcher管理“我订阅了哪些Dep”,并定义收到通知后做什么(如重新渲染、执行回调) -
Observer则是初始化阶段的“装配工”,递归遍历 data,为每个属性创建Dep并绑定get/set
三者缺一不可,共同构成 Vue2 响应式的最小闭环。
实际开发中容易忽略的关键点
理解这套机制,能更好应对常见问题:
- 新增对象属性不会响应:因为初始化时没为它创建
Dep,要用this.$set(obj, key, val) - 直接通过索引修改数组项无效:数组本身被劫持的是方法(
push、splice等),索引赋值不触发set,也要用$set - 深层嵌套对象需逐层劫持:
Observer是递归执行的,但前提是初始 data 中已存在该路径
这套设计在 Vue2 中稳定运行多年,虽有局限,但逻辑清晰、职责分明。真正掌握它,就掌握了 Vue 数据驱动视图的核心脉络。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










