vue 2 的响应式核心是 object.defineproperty 的 getter/setter:getter 负责依赖收集,将当前 watcher 加入 dep;setter 负责派发更新,触发 dep.notify() 并异步批量刷新视图。

Vue 2 的依赖收集与派发更新,本质是围绕 Object.defineProperty 的存取描述符(getter/setter)构建的一套响应式闭环。它不是抽象概念,而是由几行关键代码驱动的真实执行流:数据读取时收集依赖,数据修改时通知更新。理解它,关键在于看清 getter 和 setter 各自承担什么角色、如何协作。
存取描述符是响应式系统的物理入口
Vue 2 用 defineReactive 为每个响应式属性定义存取描述符:
-
getter 不只是返回值,它触发
dep.depend(),把当前正在计算的 Watcher(比如组件 render 函数)记进这个属性专属的 Dep 实例中; -
setter 不只是赋新值,它调用
dep.notify(),遍历该 Dep 的所有 subs(即被收集过的 Watcher),逐个执行update(); - 每个属性独享一个
Dep实例,确保“改 message 不会误通知 watch username 的回调”——依赖隔离靠的就是这个一对一映射。
依赖收集发生在“读取时”,且依赖的是执行上下文
收集动作本身不主动发生,而是在某个函数执行过程中“顺手记一笔”。例如组件首次渲染时:
-
vm._render()执行,访问this.message; - 触发
message的 getter,此时全局变量Dep.target指向当前正在运行的组件 Watcher; -
dep.depend()就把该 Watcher 加入自己的subs集合; - computed 或 watch 的回调同理:它们在初始化时也会主动求值一次,从而完成自身对数据的依赖登记。
派发更新不是立刻 DOM 重绘,而是异步调度
setter 中的 dep.notify() 并不直接刷新视图,它只做一件事:把对应 Watcher 推入更新队列。
- 多个数据变动可能在一次事件循环内连续触发,但 Vue 会去重合并,避免重复 render;
- 所有待更新 Watcher 被统一塞进
queueWatcher,再由nextTick包裹,在下一个宏任务开始前批量执行flushSchedulerQueue; - 真正重新渲染发生在
watcher.run(),它会再次触发 getter——这又开启新一轮依赖收集,为下一次更新做准备。
嵌套对象和数组需额外处理,靠的是递归 observe 与依赖补全
单纯给顶层属性加 getter/setter 不够,深层属性也要响应:
- 在 defineReactive 的 getter 中,若值是对象或数组,会调用
observe(val)对其递归响应化; - 数组通过重写原型方法(push/pop 等)拦截变更,并在内部手动触发
dep.notify(); - 访问数组元素如
arr[0]时,dependArray会遍历数组每一项,对每个响应式子项也调用dep.depend(),防止漏收依赖。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











