大家好,我是作曲家种太阳,今天我们来手把手去看看 Vue 3 响应式系统中非常核心的一部分:watch 是怎么个执行流程和触发流程,深入了解watch的原理和设计理念
watch 是什么?
watch
是 Vue3 中用于监听响应式数据变化的 API,当被监听的数据发生变化时,它会触发用户定义的回调函数。它适用于需要在数据变化时执行异步或副作用逻辑的场景。
watch 接收三个参数:
- source:响应式对象、getter 函数、ref 或数组等。
- cb:当监听的数据变化时要执行的回调函数。
- options :配置项,如:
immediate
: 是否立即执行一次 cb。deep
: 是否进行深度监听。
源码解读:watch 的执行过程
1️⃣ watch 函数入口
- 源码路径:
packages/runtime-core/src/apiWatch.ts
- 核心调用的是
doWatch
方法
watch 中做了以下处理:
- 判断 source 类型,生成 getter(一般是
() => source
) - 若为 reactive 对象,默认开启 deep 监听
- 创建 baseGetter
- 创建
job
函数:数据变化时触发的回调逻辑 - 创建调度器
scheduler = () => queuePreFlushCb(job)
- 使用 ReactiveEffect 包装 getter,并附带 scheduler
- 返回一个 teardown 回调,用于后续停止监听
2️⃣ 响应式数据触发 setter
- 当
reactive
数据变更时,触发trigger
→triggerEffect
- 若 effect 有
scheduler
,则执行scheduler()
- 此 scheduler 调用
queuePreFlushCb(job)
,将任务放入微任务队列
3️⃣ queuePreFlushCb 函数执行
ts
queuePreFlushCb(job) → queueCb(job, pendingPreFlushCbs)
→ queueFlush() → Promise.resolve().then(flushJobs)
此过程核心在于:
- 将 job 推入队列
pendingPreFlushCbs
- 使用微任务 Promise 确保异步执行
flushJobs
4️⃣ flushJobs 函数
- 触发 flushPreFlushCbs → 执行所有
pendingPreFlushCbs
- job 被调用 → 触发 effect.run()
此时 effect.run() 会重新调用 getter,获取新的值。
- 然后调用
callWithAsyncErrorHandling(cb, ...)
执行回调函数 cb - 参数为:
(newValue, oldValue)
watch 核心组成
整个 watch 的运行分为 4 大块:
watch
函数初始化逻辑- reactive 的 setter 触发 trigger
- 调度系统:queuePreFlushCb + flushJobs
- job 执行,运行 effect,触发 cb 回调
调度系统实现机制
watch 中使用了 Vue3 的调度系统,该系统包含两个核心机制:
💤 lazy 懒执行
当 effect
设置 lazy: true
时,不会立即执行 run()
方法,而是等到手动触发或依赖变动时才执行。
🧠 scheduler 调度器
调度器控制两件事:
-
控制执行顺序: 利用 queue + Promise.then 实现异步控制,确保 DOM 更新前统一 flush。
-
控制执行规则: 例如:如果连续赋值多次,只执行一次 effect(节流/去抖)
实现关键:
ts
export function queuePreFlushCb(cb: Function) {
queueCb(cb, pendingPreFlushCbs)
}
function queueFlush() {
if (!isFlushPending) {
isFlushPending = true
currentFlushPromise = resolvedPromise.then(flushJobs)
}
}
function flushJobs() {
isFlushPending = false
flushPreFlushCbs()
}
function flushPreFlushCbs() {
const cbs = [...new Set(pendingPreFlushCbs)]
pendingPreFlushCbs.length = 0
for (const cb of cbs) cb()
}
通过微任务机制控制更新节奏,实现 watch 调度的异步安全。
总结
watch
是用于观察响应式数据变化的 API,适合执行副作用逻辑。- 它底层基于
ReactiveEffect
创建 effect,并使用scheduler
控制回调时机。 - watch 的调度机制基于异步队列和微任务,确保回调顺序和性能。
- Vue3 的响应式系统通过 effect、调度器、队列等模块协同工作,保证了数据驱动视图更新的高效和灵活。