watch与watchEffect的核心区别
watch 和 watchEffect 都是 Vue 中用于侦听响应式数据变化的工具,但它们的依赖追踪方式、执行时机和使用场景有显著区别。
| 特性 | watch | watchEffect |
|---|---|---|
| 依赖追踪方式 | 显式指定侦听的数据源(如 ref、reactive 属性) | 隐式追踪回调函数内部的响应式依赖 |
| 执行时机 | 默认懒执行(首次不执行,仅在数据源变化时执行) | 立即执行(组件挂载时立即执行一次) |
| 回调参数 | 提供新值和旧值(如 (newVal, oldVal) => {}) | 无参数(通过函数内部的依赖自动触发) |
| 适用场景 | 需要知道数据变化前后的值,或侦听特定数据源 | 需要根据多个依赖执行副作用(如数据请求) |
| 停止侦听 | 返回一个停止函数(如 const stop = watch(...)) | 返回一个停止函数(如 const stop = watchEffect(...)) |
使用
watch使用
watch 用于显式侦听一个或多个响应式数据源,当数据源变化时执行回调函数。
- 侦听单个
ref
import { ref, watch } from 'vue';
const count = ref(0);
// 侦听 count 的变化
watch(count, (newVal, oldVal) => {
console.log(`count 从 ${oldVal} 变为 ${newVal}`);
});
// 修改 count,触发 watch 回调
count.value = 1; // 输出:count 从 0 变为 1
- 侦听多个
ref
import { ref, watch } from 'vue';
const a = ref(0);
const b = ref(0);
// 侦听多个数据源
watch([a, b], ([newA, newB], [oldA, oldB]) => {
console.log(`a 从 ${oldA} 变为 ${newA}`);
console.log(`b 从 ${oldB} 变为 ${newB}`);
});
// 修改 a 或 b,触发 watch 回调
a.value = 1; // 输出:a 从 0 变为 1,b 从 0 变为 0
b.value = 2; // 输出:a 从 1 变为 1,b 从 0 变为 2
- 侦听
reactive对象的属性
import { reactive, watch } from 'vue';
const state = reactive({ count: 0, name: 'Vue' });
// 侦听 state.count 的变化
watch(
() => state.count,
(newVal, oldVal) => {
console.log(`count 从 ${oldVal} 变为 ${newVal}`);
}
);
// 修改 state.count,触发 watch 回调
state.count = 1; // 输出:count 从 0 变为 1
- 深度侦听
reactive对象
import { reactive, watch } from 'vue';
const state = reactive({
user: { name: 'Vue', age: 3 }
});
// 深度侦听 state.user 的变化
watch(
() => state.user,
(newVal, oldVal) => {
console.log('user 对象发生变化', newVal);
},
{ deep: true } // 开启深度侦听
);
// 修改 state.user.age,触发 watch 回调
state.user.age = 4; // 输出:user 对象发生变化 { name: 'Vue', age: 4 }
- 立即执行
watch
import { ref, watch } from 'vue';
const count = ref(0); 、
// 立即执行 watch(组件挂载时执行一次)
watch(
count,
(newVal, oldVal) => {
console.log(`count 当前值为 ${newVal}`);
},
{ immediate: true } // 开启立即执行
);
// 输出:count 当前值为 0
watchEffect使用
watchEffect 用于隐式侦听回调函数内部的响应式依赖,当任何依赖变化时执行回调函数。
- 基本使用
import { ref, watchEffect } from 'vue';
const count = ref(0);
const name = ref('Vue');
// 隐式侦听 count 和 name 的变化
watchEffect(() => {
console.log(`count: ${count.value}, name: ${name.value}`);
});
// 输出:count: 0, name: Vue
// 修改 count,触发 watchEffect 回调
count.value = 1; // 输出:count: 1, name: Vue
// 修改 name,触发 watchEffect 回调
name.value = 'React'; // 输出:count: 1, name: React
- 停止侦听
import { ref, watchEffect } from 'vue';
const count = ref(0);
// 停止侦听
const stop = watchEffect(() => {
console.log(`count: ${count.value}`);
});
// 修改 count,触发 watchEffect 回调
count.value = 1; // 输出:count: 1
// 停止侦听
stop();
// 修改 count,不再触发 watchEffect 回调
count.value = 2; // 无输出
- 清除副作用
watchEffect 支持清除副作用(如取消数据请求、清除定时器等)。
import { ref, watchEffect } from 'vue';
const id = ref(0);
watchEffect((onInvalidate) => {
// 模拟数据请求
const timer = setTimeout(() => {
console.log(`请求数据,id: ${id.value}`);
}, 1000);
// 清除副作用(组件卸载或依赖变化时执行)
onInvalidate(() => {
clearTimeout(timer);
console.log('清除定时器');
});
});
// 修改 id,触发 watchEffect 回调
id.value = 1;
// 输出:清除定时器,然后 1 秒后输出:请求数据,id: 1
总结
watch:适用于需要显式侦听特定数据源,并需要知道数据变化前后值的场景。watchEffect:适用于需要根据多个依赖执行副作用,且不需要关心数据变化前后值的场景。
在实际开发中,应根据具体需求选择合适的侦听器:
- 如果需要侦听特定数据的变化并获取新旧值,使用
watch。 - 如果需要根据多个依赖执行副作用(如数据请求),使用
watchEffect。