Vue3 中的 watch 和 watchEffect 都是用于响应式数据变化的监听工具,但它们在使用方式、触发机制、停止监听的灵活性以及副作用清理等方面存在显著差异。以下是详细对比及适用场景分析:


1. 基本用法与语法

watch
  • 显式指定依赖源:需要明确传入要监听的响应式对象(如 refreactive 或 getter 函数)。
  • 返回停止函数:调用时会返回一个 stop 函数,用于手动停止监听。
  • 支持深度监听与配置选项:可通过参数配置 immediate(初始立即触发)、deep(深度监听)等。
import { watch, ref } from 'vue';

const count = ref(0);

// 监听单个 ref
watch(count, (newVal, oldVal) => {
  console.log(`Count changed: ${oldVal} -> ${newVal}`);
});

// 监听 reactive 对象的特定属性
const obj = reactive({ name: 'Vue' });
watch(() => obj.name, (newVal) => {
  console.log(`Name updated to ${newVal}`);
});

// 返回 stop 函数
const stopWatchCount = watch(count, () => { ... });
stopWatchCount(); // 手动停止监听

watch 监听对象的某个属性变化时,需要写成函数形式,以确保正确追踪依赖关系

watchEffect
  • 自动追踪依赖:无需显式指定监听目标,内部会自动收集在回调中访问的响应式依赖。
  • 无返回值:不提供手动停止监听的 stop 函数。
  • 简化语法:适合快速实现依赖驱动的逻辑。
import { watchEffect } from 'vue';

const count = ref(0);
watchEffect(() => {
  console.log(`Current count: ${count.value}`); // 自动追踪 count 的变化
});

2. 核心区别

特性watchwatchEffect
依赖来源必须显式传入监听目标自动追踪回调内使用的响应式依赖
停止监听返回 stop 函数可手动取消无直接返回值,需外部管理
触发时机仅当被监听的目标变化时触发初始化时立即执行一次,后续依赖变化再触发
深度监听配置支持 deepimmediate 等选项不支持额外配置项
适用场景精确控制监听逻辑、复杂状态同步简单场景下的自动化依赖处理

3. 典型应用场景

优先选择 watch 的场景
  • 精准控制监听范围:例如只监听某个 ref 或 reactive 对象的特定属性。
  • 需要历史值对比:获取变化前后的值(newValoldVal)。
优先选择 watchEffect 的场景
  • 自动化依赖管理:当多个响应式数据共同影响同一结果时,避免逐个添加监听。
  • 简洁的状态同步:如实时计算总价(商品数量 × 单价),只需关注最终表达式。
  • 减少冗余代码:无需手动维护复杂的依赖列表。
const items = ref([{ price: 10, quantity: 2 }, { price: 5, quantity: 3 }]);
watchEffect(() => {
  const total = items.value.reduce((sum, item) => sum + item.price * item.quantity, 0);
  console.log(`Total cost: ${total}`);
});

4. 注意事项

  • 性能考量watchEffect 因自动依赖追踪可能导致不必要的重复执行,尤其在大型对象或深层嵌套场景下需谨慎。
  • 调试难度:由于依赖隐式化,排查意外触发的原因比 watch 更困难。

总结

需求特征推荐方案理由
需要知道变化前后的值watch提供 newVal 和 oldVal 参数
监听特定属性而非整个对象watch + getter 函数精确控制监听粒度
多依赖联动且无需中间状态watchEffect自动聚合依赖,代码更简洁
需要手动取消监听watch返回 stop 函数便于资源释放
初始化时即需执行副作用watch(..., { immediate: true })
或 watchEffect
前者可控性强,后者更简洁

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐