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,并优化应用的性能表现。

Vue3 的响应式系统源码级 watch 与 watchEffect 的实现原理与区别 描述 watch 和 watchEffect 是 Vue3 响应式系统中用于观察数据变化并执行副作用函数的两个核心 API。它们都基于 effect 实现,但在使用方式和内部机制上有显著区别。watch 需要显式指定依赖源,支持深度监听和精确控制回调触发时机;watchEffect 则自动收集依赖,立即执行并响应所有回调函数内部访问的响应式数据变化。 解题过程 1. 核心基础:effect 函数 watch 和 watchEffect 都基于 Vue3 的底层 effect 函数 effect 会创建一个副作用函数,当函数内部访问的响应式数据变化时,effect 会重新执行 effect 通过 Proxy 的 getter 进行依赖收集,通过 setter 触发依赖更新 2. watchEffect 的实现原理 自动依赖收集 :执行 effectFn 时,访问的任何响应式属性都会被自动追踪 立即执行 :创建后立即运行一次,确保依赖关系正确建立 清理机制 :支持 onInvalidate 回调,用于清理前一次执行产生的副作用 3. watch 的实现原理 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,并优化应用的性能表现。