在 Vue 3 中,watch 函数提供了强大的监听功能,可以监听响应式数据的变化。默认情况下,watch 并不会进行深度监听,也就是说它只会检测到对象或数组的直接替换,而不会检测到其内部属性或元素的变化。如果你需要对对象或数组进行深度监听,可以通过设置 deep 选项来实现。
基本用法
假设你有一个响应式的对象或数组,并希望监听它的变化:
import { reactive, watch } from 'vue';
const state = reactive({
nested: {
count: 0
}
});
普通监听(非深度监听)
仅监听整个对象是否发生变化:
watch(() => state.nested, (newVal, oldVal) => {
console.log('state.nested changed:', newVal, oldVal);
});
在这个例子中,只有当 state.nested 被整个替换时,监听器才会触发。
深度监听
如果希望监听对象内部任何层级的变化,需要设置 deep: true:
watch(() => state.nested, (newVal, oldVal) => {
console.log('state.nested deep changed:', newVal, oldVal);
}, { deep: true });
这样,即使 state.nested.count 发生了变化,监听器也会被触发。
使用 watch 监听多个来源
你可以同时监听多个来源的数据变化,无论是简单的值、对象还是计算属性。例如:
watch([() => state.nested.count, () => state.nested.name], ([newCount, newName], [oldCount, oldName]) => {
console.log('count or name changed:', newCount, newName, oldCount, oldName);
}, { deep: true });
注意事项
-
性能考虑:深度监听会增加计算开销,因为它需要递归地检查每个嵌套级别的变化。因此,仅在必要时使用深度监听,并尽量限制监听范围。
-
旧值与新值:在深度监听的情况下,Vue 可能无法提供真正的旧值和新值对比,因为 Vue 实际上是在比较两个不同的对象引用。这意味着
oldVal和newVal可能是相同的对象引用,特别是在浅层比较时。 -
清理副作用:如果你在监听器内执行异步操作或其他有副作用的操作,请确保在组件卸载前清理这些副作用,以避免内存泄漏。可以通过返回一个清理函数来实现:
const stopWatch = watch(() => state.nested, (newVal, oldVal) => { const timer = setTimeout(() => { console.log('Asynchronous operation after state change'); }, 1000); return () => clearTimeout(timer); // 清理定时器 }, { deep: true }); // 当不再需要监听时 stopWatch();
通过合理使用 watch 的深度监听功能,你可以更精确地控制你的应用逻辑,并有效地响应复杂状态的变化。
watchEffect
在 Vue 3 中,watchEffect 是一个非常强大的函数,用于自动追踪响应式依赖并立即执行副作用。与 watch 不同,watchEffect 会自动收集其内部使用的响应式数据作为依赖项,并在这些依赖项发生变化时重新运行。
默认行为
关于深度监听,watchEffect 的一个重要特点是它默认会进行深度监听,但这种“深度监听”是基于它自动追踪到的所有响应式依赖,而不是像 watch 那样通过显式的 deep: true 选项来实现的。
具体来说:
- 当你在
watchEffect内部访问某个响应式对象或数组的属性时,Vue 会自动追踪这些属性的变化。 - 如果你修改了对象或数组内部的任何属性,
watchEffect会检测到变化并重新运行。
示例
假设你有一个嵌套的响应式对象:
import { reactive, watchEffect } from 'vue';
const state = reactive({
nested: {
count: 0,
details: {
name: 'Vue'
}
}
});
你可以使用 watchEffect 来监听任何对 state.nested 及其子属性的更改:
watchEffect(() => {
console.log('Current count:', state.nested.count);
console.log('Name in details:', state.nested.details.name);
});
在这个例子中,如果修改了 state.nested.count 或 state.nested.details.name,watchEffect 都会自动检测到这些变化并重新运行。
例如:
setTimeout(() => {
state.nested.count++; // 触发 watchEffect 重新运行
}, 1000);
setTimeout(() => {
state.nested.details.name = 'Vue 3'; // 同样触发 watchEffect 重新运行
}, 2000);
注意事项
-
性能考虑:虽然
watchEffect自动追踪所有被访问的响应式依赖,但这也意味着它可能会追踪比你实际需要更多的依赖项。因此,在大型应用中,合理控制watchEffect的范围和逻辑是很重要的,以避免不必要的计算开销。 -
清理副作用:如果你在
watchEffect内执行了异步操作或其他有副作用的操作,请确保在组件卸载前清理这些副作用,以避免内存泄漏。可以通过返回一个清理函数来实现:const stopWatchEffect = watchEffect(() => { const timer = setTimeout(() => { console.log('Asynchronous operation after state change'); }, 1000); return () => clearTimeout(timer); // 清理定时器 }); // 当不再需要监听时 stopWatchEffect(); -
对比
watch和watchEffect:watch提供更细粒度的控制,可以指定要监听的具体数据源,并且可以选择是否启用深度监听。watchEffect更加自动化,适合于快速实现简单的响应式逻辑,但可能不如watch灵活。
总结
watchEffect 在 Vue 3 中默认会自动追踪并监听所有在其作用域内被访问的响应式依赖,这相当于默认进行了深度监听。然而,这种监听方式是基于自动追踪的机制,而不是通过显式的配置选项来实现的。理解这一点可以帮助你更好地利用 watchEffect 来构建响应式的应用逻辑。

浙公网安备 33010602011771号