首先我们还是老规矩,先介绍他是什么
🐟watch(监听器)
watch用于监听特定数据的变化,并在数据变化的同时执行回调函数。可以监听一个到多个的数据变化,执行自定义的逻辑.
上面是Vue官方文档定义的,Vue3中的watch只能监听以上四种数据
🐟情况一 监视【ref】定义的【基本类型】数据
我们来写一段代码,实现简单的数字加一,
<template>
<div>
当前数字为<h2>{{ sum }}</h2>
<button @click="changesum">add</button>
</div>
</template>
<script setup>
import { ref, watch } from "vue";
let sum = ref(0);
function changesum() {
sum.value += 1;
}
watch(sum, (newValue, oldValue) => {
console.log('变化了', newValue, oldValue);
});
</script>
<style lang="scss" scoped></style>
watch函数用于监听响应式数据的变化。在你提供的代码中,watch(sum, (newValue, oldValue) => {...})的作用是监听sum这个响应式数据的变化,其中newvalue是变化后的,oldvalue是变化前的,点击button,看控制台的输出
很多人觉得既然我的sum是ref这个方法所创建的,那为什么监视的时候,不用sum.value呢,我们来试试看
控制台输出了这个警告,我给大家翻译一下
控制台输出的是watch只能监视四种数据,即开头写的四种,但是我们监视的是sum里面的value,所以就不能成功.所以咱们还是用sum,要记住一下啦!
既然我们可以开始监视,呢么我们也可以停止监视,在 Vue.js 中,可以使用watch的返回值来停止对响应式数据的监视。为啥是用watch的返回值来停止呢,他是为了提供清晰、灵活和可维护的编程体验而设计的一种方式,符合现代前端开发对于高效、可管理代码的需求。
我们来设置如果sum>=10了我们就停止监视,下面是代码的实现
const stopwatch = watch(sum, (newValue, oldValue) => {
console.log('变化了', newValue, oldValue);
if (newValue >= 10) {
stopwatch()
}
});
你会发现到了10的时候,就停止了监视,没有输出
'变化了 X X'。
🐟情况二 监视【ref】定义的【对象类型】数据
我们都知道ref既可以创造定义基本类型也可以创造对象类型的响应式数据,我们的情况二就是针对于对象类型,我们来看一段代码
<template>
<div>
<h2>姓名:{{ person.name }}</h2>
<h2>年龄:{{ person.age }}</h2>
<button @click="changeName">改名字</button>
<button @click="changeAge">改年龄</button>
<button @click="changePerson">改整个人</button>
</div>
</template>
<script setup>
import { ref, watch } from "vue";
let person = ref({
name: '张三',
age: 18
})
const changeAge = () => {
person.value.age += 1
}
const changeName = () => {
person.value.name += "~"
}
const changePerson = () => {
person.value = { name: '李四', age: 30 }
}
watch(person, (newValue, oldValue) => {
console.log('对象变化了', newValue, oldValue);
})
</script>
<style lang="scss" scoped></style>
我们定义了一个含有age,name属性的对象Person,然后做了三个改变它的按钮,分别是改变姓名,年龄和整个人,还对Person进行了监视,我们来看一下,这三个按钮哪几个会触发监控,
分别点击了前两个按钮,控制台都没有任何输出,点击最后一个按钮
控制台输出的内容表示正在监视的对象
person发生了变化。
Proxy(Object) {name: '李四', age: 30}是新值newValue,表示对象变化后的状态,即当前person的值为一个包含name属性为“李四”、age属性为 30 的对象。Proxy(Object) {name: '张三~', age: 19}是旧值oldValue,表示对象变化前的状态,即之前person的值为一个包含name属性为“张三~”、age属性为 19 的对象。
我们发现它监视的是对象的地址值,那如果我们想监视他的内部属性呢?
我们可以这样做在里面再加一个参数{deep:true}
watch(person, (newValue, oldValue) => {
console.log('对象变化了', newValue, oldValue);
},{deep:true})
再去试一试看,
发现现在就算改变内部属性,也可以引发监控,但是我们发现他们里面的值,每次
newValue和oldValue都是一模一样的,这是为什么呢?
当你在changeAge和changeName方法中直接修改对象的属性时,Vue 的响应式系统会检测到这个变化,并触发相关的依赖更新。但是,由于只是修改了对象的属性而没有改变对象的引用,所以在watch的回调中,新旧值指向的是同一个对象。
例如,在changeName方法中给person.value.name添加一个 “~”。Vue 检测到这个变化后,会更新依赖于person.name的地方。在watch的回调中,newValue和oldValue实际上都是指向同一个person对象的引用,只是这个对象的name属性值发生了变化。
在
changePerson方法中,直接将person.value重新赋值为一个全新的对象,这就导致了新旧值是完全不同的两个对象。
这个时候还可以再加一个参数
watch(person, (newValue, oldValue) => {
console.log('对象变化了', newValue, oldValue);
},{deep:true,immediate:true})
这个
immediate: true 的调用,watch 会在创建时立即触发一次回调函数,传入当前被监视的值作为 newValue,旧值 oldValue 为 undefined。这在一些场景下很有用,比如你希望在组件创建后立即执行一些基于初始值的操作。
除了这两个,watch还有其他的配置选项,大家可以去自行了解,我就不多做赘述了
🐟情况三 监视【reactive】定义的【对象类型】数据
我们用reactive来创建一个响应式数据
<template>
<div>
<h2>姓名:{{ person.name }}</h2>
<h2>年龄:{{ person.age }}</h2>
<button @click="changeName">改名字</button>
<button @click="changeAge">改年龄</button>
<button @click="changePerson">改整个人</button>
</div>
</template>
<script setup>
import { ref, watch } from "vue";
let person = ref({
name: '张三',
age: 18
})
const changeAge = () => {
person.value.age += 1
}
const changeName = () => {
person.value.name += "~"
}
const changePerson = () => {
person.value = { name: '李四', age: 30 }
}
watch(person, (newValue, oldValue) => {
console.log('对象变化了', newValue, oldValue);
})
</script>
<style lang="scss" scoped></style>
这个时候我们点哪几个按钮会实现他们的更新呢,我们来试一试
发现三个都可以被
watch监视到,我们发现reactive定义的响应式数据是默认开启深度监视的
<template>
<div>
<h2>测试 :{{ obj.a.b.c }}</h2>
<button @click="test">修改obj.a.b.c</button>
</div>
</template>
<script setup>
let obj = reactive({ a: { b: { c: 666 } } })
const changeAge = () => {
person.age += 1
const test = () => {
obj.a.b.c = 888
}
watch(obj, (newValue, oldValue) => {
console.log('变化了', newValue, oldValue);
})
</script>
<style lang="scss" scoped></style>
点击修改obj.a.b.c按钮
说明他就是隐式开启深度监视,且无法被隐蔽!
🐟情况四 监视【reactive,ref】定义的【对象类型】中的某个属性
老样子先来一段代码看看
<template>
<div>
<h2>姓名:{{ person.name }}</h2>
<h2>年龄:{{ person.age }}</h2>
<h2>汽车:{{ person.car }}</h2>
<button @click="changeName">改名字</button>
<button @click="changeAge">改年龄</button>
<button @click="changeC1">改c1</button>
<button @click="changeC2">改c2</button>
<button @click="changeCar">改car</button>
</div>
</template>
<script setup>
import { reactive, watch } from "vue";
let person = reactive({
name: '张三',
age: 18,
car: {
c1: '奔驰',
c2: '宝马'
}
})
const changeName = () => {
person.name += "~"
}
const changeAge = () => {
person.age += 1
}
const changeC1 = () => {
person.car.c1 = '奥迪'
}
const changeC2 = () => {
person.car.c2 = '大众'
}
const changeCar = () => {
person.car = {
c1: '雅迪',
c2: '艾玛'
}
}
watch(person, (newValue, oldValue) => {
console.log('变化了', newValue, oldValue);
})
</script>
<style lang="scss" scoped></style>
如果这个时候我们只想监视他的name,其他的不想监视,可不可以用person.value,
不行,他不是上面的四种数据之一,呢我们只能想办法让他变通一下了,可以让他用getter函数(就是用来返回一个值)来实现,
我们直接用一个匿名函数
watch(()=>{person.name}, (newValue, oldValue) => {
console.log('person.name变化了', newValue, oldValue);
})
只有改名字才会触发监视,我们得到一下结论,该属性为基本类型时,要写成函数式
但如果要监视对象类型呢?
watch(person.car, (newValue, oldValue) => {
console.log('person.car变化了', newValue, oldValue);
})
我们发现person.car可以直接写,也不会报错,这是因为person.car是一个对象,但是奇怪的又来了,只有按钮改c1和改c2被监视了,改car的按钮并没有被监视
我们试试用
匿名函数
watch(()=>person.car, (newValue, oldValue) => {
console.log('person.car变化了', newValue, oldValue);
})
这个时候变成了,按钮改c1和改c2没有被监视了,改car的按钮被监视了,这个时候我们再加一个参数
watch(()=>person.car, (newValue, oldValue) => {
console.log('person.car变化了', newValue, oldValue),{deep:true}
})
这个时候,三个按钮就全都生效了!
监视的要是对象里的属性,呢么最好写函数式,注意点:若是对象监视的是地址值,则需要关注对象内部,需要手动开启深度监视!
🐟情况五 监视上述多个数据
- 可以在
watch的第一个参数中传入一个数组,数组中包含多个要监视的表达式。
watch([() => person.car, () => person.name], (newValue, oldValue) => {
console.log('person.car变化了', newValue, oldValue);
}, { deep: true })
除了person.age都可以监视!
🐟END
总之,watch 在 Vue 中是一个非常强大的工具,可以根据不同的需求来监视各种类型的响应式数据。在使用时,需要根据具体情况选择合适的监视方式,并注意性能和逻辑的复杂性。