前言
笔者最近在准备面试,发现好多面试官都喜欢问vue中watch
和watchEffect
的区别,正好最近也在写vue相关的文章,于是乎准备用这篇文章彻底讲清watch
和watchEffect
及它们的区别。
watch的用法
watch
在官方文档的解释如下:侦听一个或多个响应式数据源,并在数据源变化时调用所给的回调函数。 在我看来他的作用无非是:监视数据的变化。它只能监视以下四种数据:
1.ref
定义的数据
2.reactive
定义的数据
3.函数返回一个值(getter函数)
4.一个包含上述内的数组
现在让我来一一介绍这几种用法
监视ref
定义的数据
基本类型数据
我们知道,ref
可以定义基本类型数据和对象类型数据,那显而易见watch
也能监听这两种事件,先来看监视ref
定义的基本类型数据,直接写数据名即可,其本质上监视的是value
的变化。
代码示例:
xml
<template>
<div class="person">
<h2>当前求和为:{{ sum }}</h2>
<button @click = changeSum>点我 sum +1</button>
</div>
</template>
<script lang="ts" setup >
import { ref,watch } from 'vue'
let sum = ref(0)
function changeSum() {
sum.value+=1
}
const stopWatch = watch(sum, (newValue, oldValue) => {
console.log(`sum 的值从 ${oldValue} 变为 ${newValue}`);
if (newValue > 10) {
stopWatch(); // 停止监听
}
});
</script>
对象类型数据
要监听ref
定义的对象类型数据也是直接写数据名就行了,不过这和监视基本类型数据不同的是,这个监视的是对象的地址值,如果想监视对象内部的数据,要手动开启深度监视。 注意:
- 如果修改的是
ref
定义的对象中的属性,newValue
和oldValue
都是新值,因为它们是同一个对象。 - 如果修改的是
ref
定义的对象,newValue
是新值,oldValue
是旧值,因为它们已经不是同一个对象了 代码示例:
xml
<template>
<div class="person">
<h2>姓名: {{ person.name }}</h2>
<h2>姓名: {{ person.age }}</h2>
<button @click="changeName">修改名字</button>
<button @click="changeAge">修改年龄</button>
<button @click="changePerson">修改人</button>
</div>
</template>
<script lang="ts" setup >
import { ref,watch } from 'vue'
let person = ref({
name: '张三',
age: 18
})
let changeName = () => {
person.value.name = '李四'
}
let changeAge = () => {
person.value.age += 1
}
let changePerson = () => {
person.value = {
name: '李四',
age: 20
}
}
watch(person,(newValue,oldValue)=>{
console.log(newValue,oldValue)
},{deep:true})
可以看到当单独修改姓名或年龄时控制台上oldValue
和newValue
一直都是相等的,只有修改人的时候才会二者才会出现不一样。
监视reactive定义的数据
当监视reactive
定义的对象类型数据时,是默认开启了深度监视的 代码示例:
xml
<template>
<div class="person">
<h2>姓名: {{ person.name }}</h2>
<h2>年龄: {{ person.age }}</h2>
<button @click="changeName">修改名字</button>
<button @click="changeAge">修改年龄</button>
<button @click="changePerson">修改人</button>
<h2>测试数据:{{obj.a.b.c}}</h2>
<button @click = "test">修改obj.a.b.c</button>
</div>
</template>
<script lang="ts" setup >
import { reactive, watch } from 'vue'
let person = reactive({
name: '张三',
age: 18
})
let obj = reactive({
a:{
b:{
c:1
}
}
})
let changeName = () => {
person.name = '李四'
}
let changeAge = () => {
person.age += 1
}
let changePerson = () => {
Object.assign(person,{
name:'李四',
age:50
})
}
let test = () => {
obj.a.b.c = 2
}
watch(person,(newValue,oldValue)=>{
console.log(newValue,oldValue)
},)
watch(obj,(newValue,oldValue)=>{
console.log(newValue,oldValue)
},)
watchEffect用法
再来看看watchEffect
的用法,官网中是这样介绍的:立即运行一个函数,同时响应式地追踪其依赖,并在依赖更改时重新执行该函数。 简单来说就是watchEffect
不用明确指出监视的数据,函数中用到哪些属性,那就直接监视这些属性就行了
代码示例:
xml
<template>
<div class="smart-home">
<h5>需求:当房间亮度低于30%或当前时间为晚上8点,则自动开启灯光</h5>
<h5>当前亮度:{{ brightness }}%</h5>
<h5>当前时间:{{ currentTime }}</h5>
<button @click="increaseBrightness">增加亮度(5%)</button>
<button @click="decreaseBrightness">降低亮度(5%)</button>
<button @click="advanceTime">增加一小时</button>
</div>
</template>
<script lang="ts" setup name="SmartHome">
import { ref, watch, watchEffect,computed } from 'vue';
let brightness = ref(40);
let hour = ref(19);
const currentTime = computed(() => `${hour.value}:00`);
function increaseBrightness() {
brightness.value += 5;
}
function decreaseBrightness() {
brightness.value -= 5;
}
function advanceTime() {
hour.value += 1;
}
// 使用watch实现,需要明确的指出要监视brightness和hour
watch([brightness, hour], (values) => {
const [newBrightness, newHour] = values;
if (newBrightness <= 30 || newHour === 20) {
console.log('自动开启灯光');
}
});
// 使用watchEffect实现就不用
const stopWatch = watchEffect(() => {
if (brightness.value <= 30 || hour.value === 20) {
console.log(`当前亮度: ${brightness.value}%`);
console.log('自动开启灯光');
}
if (brightness.value >= 50 || hour.value > 22) {
console.log('环境已恢复正常,停止监视');
stopWatch();
}
});
</script>
二者对比
watch
-
显式依赖:需要明确指定要监听的数据源。当这些依赖数据发生变化时,会执行相应的回调函数。
-
获取原值 :在监视由
ref
定义的响应式数据时,可以访问到变化前的原始值。 -
详细配置:不仅需要指定要监视的具体属性,还需定义相应的回调函数来处理数据变化。
watchEffect
- 自动依赖收集:无需手动指定监听的数据源,它会自动检测并收集在其回调函数中使用的所有响应式依赖。一旦这些依赖发生变更,回调函数将重新执行。 。
- 仅提供新值:无法直接获取到变化前的值,只能访问到变化后的最新值。
- 简化配置:不需要明确指出要监视哪些属性,只要在回调函数中使用了某个响应式数据,就会自动监视该数据的变化。这使得代码更加简洁和易于维护。
小结
在Vue 3中,watch
和watchEffect
都是用于监听响应式数据变化的强大工具,但它们各自有着不同的特点和适用场景。理解这两者的区别以及如何有效地使用它们,对于构建高效、可维护的Vue应用程序至关重要。
选择使用watch
还是watchEffect
,主要取决于具体的应用需求和个人偏好。如果需要对特定数据进行细致的监听并处理前后状态的变化,watch
是更好的选择;而当希望快速设置响应式依赖,简化代码逻辑时,watchEffect
则提供了更为便捷的方式。通过合理运用这两种监听机制,可以极大地提升Vue应用的交互性和响应性,为用户提供更加流畅和智能的体验。