Vue3 的响应式系统源码级 watch 与 watchEffect 的实现原理与区别
字数 1207 2025-11-24 08:05:35
Vue3 的响应式系统源码级 watch 与 watchEffect 的实现原理与区别
描述
watch 和 watchEffect 是 Vue3 响应式系统中用于观察数据变化并执行副作用函数的两个核心 API。它们都基于 effect 实现,但在使用方式和内部机制上有显著区别。watch 需要显式指定依赖源,支持深度监听和精确控制回调触发时机;watchEffect 则自动收集依赖,立即执行并响应所有回调函数内部访问的响应式数据变化。
解题过程
1. 核心基础:effect 函数
- watch 和 watchEffect 都基于 Vue3 的底层 effect 函数
- effect 会创建一个副作用函数,当函数内部访问的响应式数据变化时,effect 会重新执行
- effect 通过 Proxy 的 getter 进行依赖收集,通过 setter 触发依赖更新
2. watchEffect 的实现原理
// 简化版实现逻辑
function watchEffect(effectFn, options) {
// 创建响应式副作用函数
const effect = new ReactiveEffect(effectFn);
// 立即执行一次,建立依赖关系
effect.run();
// 返回停止监听的函数
return () => effect.stop();
}
- 自动依赖收集:执行 effectFn 时,访问的任何响应式属性都会被自动追踪
- 立即执行:创建后立即运行一次,确保依赖关系正确建立
- 清理机制:支持 onInvalidate 回调,用于清理前一次执行产生的副作用
3. watch 的实现原理
// 简化版实现逻辑
function watch(source, cb, options = {}) {
let getter;
let oldValue;
// 处理不同类型的 source
if (isRef(source)) {
getter = () => source.value;
} else if (isReactive(source)) {
getter = () => source;
// 默认深度监听
options.deep = true;
} else if (isFunction(source)) {
getter = source;
}
// 处理深度监听
if (options.deep) {
const baseGetter = getter;
getter = () => traverse(baseGetter());
}
// 封装回调触发逻辑
const job = () => {
const newValue = effect.run();
cb(newValue, oldValue, onInvalidate);
oldValue = newValue;
};
// 创建 effect
const effect = new ReactiveEffect(getter, job);
// 初始收集依赖
oldValue = effect.run();
return () => effect.stop();
}
4. 关键区别分析
依赖收集方式:
- watchEffect:自动收集,执行期间所有访问的响应式数据都会被追踪
- watch:显式指定,只能监听指定的数据源(ref、reactive、getter 函数)
执行时机:
- watchEffect:立即同步执行,无初始值概念
- watch:默认懒执行,可通过
{immediate: true}立即执行
深度监听:
- watchEffect:不支持配置深度监听,自动追踪所有层级
- watch:支持
{deep: true}配置,可控制是否深度监听
新旧值获取:
- watchEffect:无法获取变化前的旧值
- watch:回调函数可获取新旧值进行对比
5. 性能优化机制
watchEffect 的依赖更新:
- 每次执行都会重新建立完整的依赖关系
- 自动清理不再使用的依赖,避免内存泄漏
watch 的缓存优化:
- 依赖源变化时才执行回调,减少不必要的计算
- 支持
{flush: 'sync' | 'pre' | 'post'}控制回调执行时机
6. 实际应用场景
watchEffect 适用场景:
- 不关心具体哪个数据变化,只需要在相关数据变化时执行操作
- 需要自动追踪多个相关数据的场景
- 简单的副作用逻辑,如日志记录、DOM 操作
watch 适用场景:
- 需要对比新旧值进行特定逻辑处理
- 需要精确控制监听的数据源和触发条件
- 性能敏感场景,避免不必要的回调执行
通过理解 watch 和 watchEffect 的底层实现原理,可以更好地根据具体需求选择合适的 API,并优化应用的性能表现。