watch
和 watchEffect
是两个超实用的响应式工具,它们能帮我们处理副作用。接下来咱就详细唠唠它们的使用方法、区别以及面试考点。
一、watch 的使用
watch
这个方法主要用来监听响应式数据的变化,一旦数据有变动,就会执行相应的回调函数。它的使用场景非常广泛,比如当用户输入框里的内容改变了,我们要实时更新某个数据;或者某个状态值变化时,去触发一些异步操作。
基本用法
下面是监听单个响应式数据的例子:
javascript
import { ref, watch } from 'vue';
const count = ref(0);
watch(count, (newValue, oldValue) => {
console.log(`新值: ${newValue}, 旧值: ${oldValue}`);
});
count.value++; // 这里改变了count的值,会触发watch的回调函数
在这个例子里,watch
接收两个参数,第一个是要监听的响应式数据 count
,第二个是回调函数,回调函数里有两个参数,newValue
是新的值,oldValue
是旧的值。
监听多个数据源
要是你想同时监听多个响应式数据,也是可以的。把要监听的数据放在一个数组里就行:
ini
import { ref, watch } from 'vue';
const count1 = ref(0);
const count2 = ref(0);
watch([count1, count2], ([newCount1, newCount2], [oldCount1, oldCount2]) => {
console.log(`count1新值: ${newCount1}, count1旧值: ${oldCount1}`);
console.log(`count2新值: ${newCount2}, count2旧值: ${oldCount2}`);
});
count1.value++;
count2.value++;
这里的回调函数接收的参数也是数组形式,分别对应新值和旧值。
深度监听
如果你要监听的是一个对象,并且想在对象内部属性变化时也触发回调,就得开启深度监听:
javascript
import { ref, watch } from 'vue';
const obj = ref({
name: '张三',
age: 20
});
watch(obj, (newObj, oldObj) => {
console.log('对象发生了变化');
}, { deep: true });
obj.value.age = 21; // 改变对象内部属性,会触发回调
通过设置 { deep: true }
这个选项,就能实现深度监听了。
即时回调
有时候,我们希望在组件挂载时就执行一次回调函数,而不只是在数据变化时执行。这时候可以设置 immediate: true
选项:
javascript
import { ref, watch } from 'vue';
const count = ref(0);
watch(count, (newValue, oldValue) => {
console.log(`新值: ${newValue}, 旧值: ${oldValue}`);
}, { immediate: true });
这样在组件挂载时,回调函数就会马上执行一次。
二、watchEffect 的使用
watchEffect
是一个更简洁的响应式副作用函数,它会自动追踪其内部使用的所有响应式数据,只要这些数据有变化,就会重新执行回调函数。
基本用法
javascript
import { ref, watchEffect } from 'vue';
const count = ref(0);
watchEffect(() => {
console.log(`当前count的值是: ${count.value}`);
});
count.value++; // 改变count的值,会触发watchEffect的回调函数
在这个例子里,watchEffect
接收一个回调函数,在回调函数里使用了 count
这个响应式数据,所以当 count
的值改变时,回调函数就会重新执行。
清理副作用
在 watchEffect
里,我们可以返回一个清理函数,用于在回调函数重新执行或者组件卸载时做一些清理工作,比如取消定时器、取消网络请求等:
javascript
import { ref, watchEffect } from 'vue';
const count = ref(0);
watchEffect((onInvalidate) => {
const timer = setInterval(() => {
console.log(count.value);
}, 1000);
onInvalidate(() => {
clearInterval(timer); // 清理定时器
});
});
count.value++;
这里的 onInvalidate
函数接收一个清理函数,当回调函数重新执行或者组件卸载时,清理函数就会被调用。
三、区别
1. 自动追踪与显式指定
watchEffect
会自动追踪其内部使用的所有响应式数据,只要这些数据有变化,就会重新执行回调函数。而 watch
需要你显式地指定要监听的数据源。比如上面 watchEffect
的例子,它会自动追踪 count
的变化;而 watch
则需要你明确告诉它要监听 count
。
2. 初始执行
watchEffect
会在组件挂载时立即执行一次回调函数,而 watch
默认情况下不会在组件挂载时执行回调,除非你设置了 immediate: true
选项。
3. 旧值访问
watch
的回调函数可以接收到新值和旧值,方便你对比数据的变化。而 watchEffect
没有直接提供旧值的访问方式,因为它更侧重于响应式数据的变化触发副作用,不太关注旧值。
4. 使用场景
watch
更适合用于需要对比新旧值的场景,比如当某个数据变化时,根据新旧值的不同做不同的处理;而 watchEffect
适合用于一些简单的副作用场景,只要相关数据变化就执行,不需要关心旧值。
四、基于面试
原理相关问题
面试官可能会问你 watch
和 watchEffect
的实现原理。简单来说,watch
是通过对指定的数据源进行依赖收集,当数据源变化时,触发对应的回调函数。而 watchEffect
是在回调函数执行时,自动收集其中使用的所有响应式数据的依赖,当这些数据变化时,重新执行回调函数。
使用场景选择
可能会给你一些具体的业务场景,让你选择用 watch
还是 watchEffect
。这时候你要根据它们的区别来判断。如果需要对比新旧值,就选 watch
;如果只是简单地在数据变化时执行操作,选 watchEffect
。
副作用清理
面试官也许会问你在 watch
和 watchEffect
里怎么清理副作用。在 watchEffect
里可以通过返回清理函数来实现;在 watch
里,如果你需要在组件卸载时做清理工作,可以在 beforeUnmount
钩子函数里进行。
性能方面
还可能会探讨它们对性能的影响。watchEffect
因为会自动追踪所有依赖,可能会导致一些不必要的回调执行。所以在性能敏感的场景下,使用 watch
显式指定依赖可能会更好。