Vue3 的响应式系统源码级 watch 与 watchEffect 的实现原理与区别
字数 1631 2025-11-19 04:25:29
Vue3 的响应式系统源码级 watch 与 watchEffect 的实现原理与区别
1. 核心概念与使用场景
watch 和 watchEffect 都是 Vue3 响应式系统中用于监听数据变化的 API,但它们的执行时机和依赖收集方式不同:
- watch:需显式指定监听的数据源和回调函数,默认惰性执行(首次不执行),可获取变化前后的值。
- watchEffect:自动收集回调函数内的响应式依赖,立即执行,无需指定数据源,但无法直接获取旧值。
2. 实现原理分析
(1)底层依赖:effect 与调度器
Vue3 的响应式系统基于 effect(副作用函数)实现。watch 和 watchEffect 本质都是创建一个自定义的 effect,通过调度器(scheduler)控制回调触发时机。
(2)watchEffect 的源码级流程
-
立即执行与依赖收集:
function watchEffect(fn, options) { // 创建 effect,并标记为 WatcherEffect const effect = new ReactiveEffect(fn, () => { // 调度器:当依赖变化时,触发回调 if (effect.active) { fn(); // 默认立即执行 } }); effect.run(); // 立即执行,收集依赖 }- 执行
fn时,会访问响应式数据,触发get操作,将当前effect关联到数据的依赖集合中。 - 依赖变化时,通过调度器重新执行
fn。
- 执行
-
清理副作用:
watchEffect可接收一个onCleanup回调,用于清理上一次执行的副作用(如取消请求)。- 实现原理:在每次重新执行
fn前,先执行上一次的onCleanup。
(3)watch 的源码级流程
-
数据源解析:
- 支持单个响应式对象(如
ref/reactive)或函数(如() => obj.foo)。 - 源码会调用
getter函数获取值,并记录旧值。
- 支持单个响应式对象(如
-
惰性执行与调度控制:
function watch(source, cb, options) { let getter = isRef(source) ? () => source.value : isFunction(source) ? source : () => traverse(source); let oldValue; const job = () => { const newValue = effect.run(); // 获取新值 cb(newValue, oldValue); // 触发回调 oldValue = newValue; }; const effect = new ReactiveEffect(getter, job); // 调度器为 job if (options.immediate) { job(); // 立即执行 } else { oldValue = effect.run(); // 仅收集依赖,不触发回调 } }- 默认情况下,首次执行
effect.run()只收集依赖,不触发回调(惰性)。 - 依赖变化时,通过调度器
job执行回调,并传入新值和旧值。
- 默认情况下,首次执行
-
深度监听:
- 若数据源为对象,默认浅层监听。设置
deep: true时,会递归遍历对象的所有属性,强制收集深层依赖。
- 若数据源为对象,默认浅层监听。设置
3. 关键区别与设计思想
| 特性 | watchEffect | watch |
|---|---|---|
| 依赖收集 | 自动收集回调中的所有响应式依赖 | 仅监听显式指定的数据源 |
| 执行时机 | 立即执行 | 默认惰性,可配置 immediate: true |
| 旧值获取 | 不支持 | 支持 |
| 适用场景 | 依赖逻辑简单、无需旧值的场景 | 需要精确控制监听源或对比旧值的场景 |
4. 性能优化与陷阱
watchEffect的依赖陷阱:- 若回调中包含条件分支,可能意外依赖不需要的数据。需手动清理无效依赖(如使用
onCleanup)。
- 若回调中包含条件分支,可能意外依赖不需要的数据。需手动清理无效依赖(如使用
watch的深度监听开销:deep: true会递归遍历整个对象,大型对象可能性能较差。可改用特定路径的getter函数(如() => obj.a.b)。
5. 总结
Vue3 通过复用 ReactiveEffect 和调度器机制,统一实现了 watch 和 watchEffect。两者的核心差异在于依赖收集的时机和粒度:
watchEffect更“智能”,适合逻辑简单的副作用。watch更“精确”,适合需要细粒度控制的场景。