watch用于精准监听,支持新旧值对比和延迟首次执行;watchEffect自动追踪依赖,立即执行且无法获取旧值,适用于简单响应式同步场景。

Vue 3 中,watch 和 watchEffect 都用于响应式状态变更的副作用处理,但设计目标和使用逻辑完全不同。选错会导致监听失效、重复执行、无法获取旧值,甚至意外触发请求。关键不是“哪个更好”,而是“哪个更合适”。
watch:精准监听,按需响应
你需要明确告诉它监听谁,它才行动。适合需要新旧值对比、只关注特定数据、或希望延迟首次执行的场景。
- 监听单个 ref:直接传 ref 变量,回调接收新值、旧值
<script setup>
import { ref, watch } from 'vue'
const search = ref('')
watch(search, (newVal, oldVal) => {
if (newVal && newVal !== oldVal) fetchResults(newVal)
})
</script>
- 监听多个源:传 ref 数组,回调参数为两个数组(新值数组、旧值数组)
-
监听 reactive 对象属性:必须用 getter 函数包装,如
() => state.count;直接监听整个 reactive 对象会自动深度监听 -
立即执行 + 深度监听:通过第三个参数配置,例如
{ immediate: true, deep: true }
watchEffect:自动追踪,即刻响应
它不关心你监听谁,只关心你在函数里读了哪些响应式数据——读到的全算数。组件挂载时立刻执行一次,之后只要其中任一依赖变化就重跑。
立即学习“前端免费学习笔记(深入)”;
- 写法极简:传一个函数,内部访问任何 ref 或 reactive 属性都会被自动收集
<script setup>
import { ref, watchEffect } from 'vue'
const a = ref(1)
const b = ref(2)
watchEffect(() => {
console.log('a + b =', a.value + b.value) // a 或 b 变化都会触发
})
</script>
- 无法获取旧值:只有当前最新状态可用
- 副作用清理靠 onInvalidate:异步操作(如请求)可能被覆盖,需用它取消前序任务
- 避免在函数内调用黑盒函数:如果函数内部读取了未预期的响应式数据,就会被意外监听,导致逻辑混乱
怎么选?看这三点
- 要新值和旧值?→ 用 watch(比如防抖搜索、状态切换判断)
- 只关心“现在什么状态”,且依赖分散?→ 用 watchEffect(比如同步表单字段、更新图表配置)
- 需要首次加载就执行,并且不想手动列依赖?→ watchEffect 更自然;若想首次不执行,watch 更可控
常见陷阱提醒
- watch 监听 reactive 对象的某个属性时,不能写
watch(state.count, ...),必须写watch(() => state.count, ...) - watchEffect 内部不要调用含响应式读取的工具函数,否则依赖关系不可控
- 两者都返回停止函数,如需手动停用(例如条件监听),记得调用:
const stop = watch(...); stop() - watch 默认不执行,watchEffect 默认执行一次——这个差异常被忽略,引发初始化逻辑缺失或重复


















