大白话解释Vue响应式三要素
好的!咱们用最接地气的大白话聊聊 Vue 响应式系统的三个核心角色:Observer(观察者)、Watcher(监听者)、Dep(依赖收集器)。想象一下它们仨是怎么配合工作的: 🎯 场景比喻:班级通知
从 Vue 2.x 源码角度分析将组件变量 a 从空值修改为 1 的完整调用栈如下:
在组件创建时,Vue 会初始化响应式数据:
// 调用栈:
Vue.prototype._init (init.js)
└── initState (state.js)
└── initData (state.js)
└── observe (observer/index.js)
└── new Observer (observer/index.js)
└── walk (observer/index.js)
└── defineReactive (observer/index.js) // 为属性 `a` 创建响应式
defineReactive 为 a 创建 getter/setter:
dep 实例(依赖收集器)。Object.defineProperty 重写 a 的访问器:
Object.defineProperty(obj, key, {
get() { /* 依赖收集 */ },
set(newVal) { /* 触发更新 */ }
})
a 的值执行 this.a = 1 时触发 setter:
// 调用栈:
this.a = 1
└── a 的 setter (defineReactive 内部)
└── dep.notify() (observer/dep.js)
└── subs[i].update() (observer/watcher.js)
└── queueWatcher (scheduler.js)
└── nextTick (scheduler.js)
└── flushSchedulerQueue (scheduler.js)
└── watcher.run (observer/watcher.js)
└── watcher.get (observer/watcher.js)
└── 组件重新渲染 (render 函数)
setter 触发:
1 是否与旧值不同(newVal !== oldVal)。dep.notify() 通知所有依赖。dep.notify():
dep.subs(存储所有订阅该属性的 Watcher)。watcher.update()。watcher.update():
queueWatcher)。nextTick 异步执行更新。flushSchedulerQueue:
watcher.run()。watcher.run() → watcher.get() → 重新执行组件的 render 函数。重新渲染:
render 函数执行时访问 a,触发 getter 重新收集依赖。在首次渲染和后续更新时,getter 负责收集依赖:
// getter 调用栈:
组件访问 a (render 函数)
└── a 的 getter (defineReactive 内部)
└── Dep.target (全局唯一 Watcher)
└── dep.depend() (observer/dep.js)
└── 将当前 Watcher 添加到 dep.subs
Dep.target:全局唯一变量,指向当前正在执行的 Watcher(如渲染 Watcher)。dep.depend():将当前 Watcher 加入 dep.subs,建立 属性 → Watcher 的依赖关系。Vue 使用异步队列合并更新:
// nextTick 流程:
queueWatcher (scheduler.js)
└── nextTick (util/next-tick.js)
└── 异步任务 (Promise/MutationObserver/setTimeout)
└── flushSchedulerQueue (scheduler.js)
a 会被合并为一次更新(避免重复渲染)。nextTick 确保在 DOM 更新后执行回调。若使用 Vue 3(基于 Proxy):
reactive 创建响应式代理。Proxy.set 拦截器,后续流程类似(依赖收集、异步更新)。Object.defineProperty,支持动态属性。Track 操作,更新通过 Trigger 操作。| 阶段 | 核心操作 | 关键函数/类 |
|---|---|---|
| 初始化 | 为 a 创建响应式 getter/setter
|
defineReactive、Dep
|
| 修改值 | 触发 setter → 通知依赖 |
dep.notify() |
| 依赖更新 | 异步队列合并更新 |
queueWatcher、nextTick
|
| 重新渲染 | 执行 render 函数 |
Watcher.run() |
整个流程体现了 Vue 响应式系统的核心:依赖收集(getter)和 派发更新(setter),通过 异步队列 优化性能。