在 Vue 3 中,watch
和 watchEffect
是响应式系统的重要工具,帮助开发者监听数据变化并执行副作用操作。为了让你更好地理解 watch
和 watchEffect
的用法及其区别,这里将详细解释它们的使用方式、适用场景以及它们在基本类型和引用类型上的监听效果。
1. watch
的用法和适用场景
watch
用于监听特定的响应式数据或计算属性变化,并执行相应的回调。适合用于处理数据变化后的副作用,例如 API 请求、数据验证等。
基本用法
watch
需要传入两个主要参数:
-
监听目标:可以是
ref
、reactive
数据,或是一个返回响应式数据的函数。 -
回调函数:在数据变化时触发,接收两个参数
newValue
和oldValue
,分别表示新值和旧值。
import { ref, watch } from 'vue';
const count = ref(0);
watch(count, (newValue, oldValue) => {
console.log(`count changed from ${oldValue} to ${newValue}`);
});
count.value = 5; // 输出:count changed from 0 to 5
使用场景
- 监听数据并执行副作用:适合处理需要监听数据变化的场景,例如在用户输入时进行数据验证或 API 调用。
2. watch
监听 ref
定义的基本类型
ref
定义的基本类型数据如 number
、string
等,在数据变化时可以直接触发 watch
回调。
const message = ref('Hello');
watch(message, (newMessage, oldMessage) => {
console.log(`Message changed from "${oldMessage}" to "${newMessage}"`);
});
message.value = 'Hello Vue 3!'; // 输出:Message changed from "Hello" to "Hello Vue 3!"
3. watch
监听 ref
定义的引用类型
ref
定义的引用类型数据(对象、数组)在监听时有两种情况:
- 监听整体引用:修改整个对象时会触发回调并返回新旧值。
-
监听内部属性:直接修改对象的属性不会触发回调,除非使用
{ deep: true }
深度监听。
const user = ref({ name: 'Alice', age: 25 });
watch(user, (newUser, oldUser) => {
console.log(`User changed from`, oldUser, `to`, newUser);
}, { deep: true });
// 修改整个对象会触发回调
user.value = { name: 'Bob', age: 30 }; // 输出:User changed from {name: 'Alice', age: 25} to {name: 'Bob', age: 30}
// 修改属性时也会触发回调,因为深度监听了
user.value.age = 26; // 输出:User changed from {name: 'Bob', age: 25} to {name: 'Bob', age: 26}
4. watch
监听 reactive
对象
reactive
适用于创建深层响应式对象,例如嵌套对象或复杂结构。通过 watch
可以监听整个对象,也可以监听对象的某个属性变化。
监听整个 reactive
对象
设置 { deep: true }
可以递归监听 reactive
对象中的每一个属性,确保任何内部属性变化都能触发回调。
import { reactive, watch } from 'vue';
const user = reactive({ name: 'Alice', age: 25, address: { city: 'NY', zip: '10001' } });
watch(user, (newUser, oldUser) => {
console.log(`User changed from`, oldUser, `to`, newUser);
}, { deep: true });
// 修改嵌套属性
user.address.city = 'LA'; // 输出:User changed from {name: 'Alice', age: 25, address: {city: 'NY', zip: '10001'}} to {name: 'Alice', age: 25, address: {city: 'LA', zip: '10001'}}
监听 reactive
对象的单个属性
使用函数来监听特定属性的变化,避免递归监听整个对象,提升性能。
watch(() => user.age, (newAge, oldAge) => {
console.log(`Age changed from ${oldAge} to ${newAge}`);
});
user.age = 27; // 输出:Age changed from 25 to 27
5. 监听多个数据
watch
可以同时监听多个数据,传入一个包含多个目标的数组即可。这种方法适合处理多个响应式数据的变化。
const firstName = ref('John');
const lastName = ref('Doe');
watch([firstName, lastName], ([newFirst, newLast], [oldFirst, oldLast]) => {
console.log(`Name changed from ${oldFirst} ${oldLast} to ${newFirst} ${newLast}`);
});
firstName.value = 'Jane'; // 输出:Name changed from John Doe to Jane Doe
lastName.value = 'Smith'; // 输出:Name changed from Jane Doe to Jane Smith
6. watchEffect
的用法
watchEffect
是 Vue 3 的一种自动依赖追踪方式,不需要手动指定依赖项。在回调函数中访问的所有响应式数据都会成为依赖,只要这些数据变化,回调函数会自动重新执行。
基本用法
import { ref, watchEffect } from 'vue';
const count = ref(1);
const doubled = ref(2);
watchEffect(() => {
console.log(`Count is ${count.value}, Double is ${doubled.value}`);
});
count.value = 2; // 输出:Count is 2, Double is 2
doubled.value = 4; // 输出:Count is 2, Double is 4
适用场景
watchEffect
适合自动响应简单的数据变化,尤其是需要自动执行的逻辑,省去了手动指定依赖项的步骤。常用于在挂载时执行的逻辑,或者需要自动收集依赖的副作用处理。
const firstName = ref('John');
const lastName = ref('Doe');
watchEffect(() => {
console.log(`Full name is ${firstName.value} ${lastName.value}`);
});
firstName.value = 'Jane'; // 输出:Full name is Jane Doe
lastName.value = 'Smith'; // 输出:Full name is Jane Smith
7. watch
vs watchEffect
的对比
特性 | watch |
watchEffect |
---|---|---|
手动指定依赖 | 需要手动指定 | 自动收集依赖 |
使用新旧值对比 | 可以获得新旧值,适合对比数据变化 | 不提供新旧值,适合自动响应数据变化 |
常用场景 | 适合监听特定数据,适用于精确控制数据变化时的副作用 | 适合快速响应简单数据变化,如在组件挂载后执行逻辑 |
深度监听 | 可以使用 { deep: true } 进行深度监听 |
无法深度监听 |
性能和灵活性 | 可精确控制依赖项更新,适合复杂逻辑 | 简单灵活,适合自动响应依赖项变化 |
总结
-
watch
:适合用于处理数据变化的副作用,尤其是异步请求、数据对比、深度监听复杂对象等场景。可以获取新旧值,灵活控制副作用逻辑。 -
watchEffect
:适合自动依赖收集的简单场景,如在组件挂载时立即执行,且依赖项会自动更新,不需要手动指定依赖项。
通过灵活运用 watch
和 watchEffect
,可以让 Vue 3 应用的数据响应更加高效,提升代码的可读性和维护性。