开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 9 天,点击查看活动详情
说点题外话
上一篇说了组合式中新增的一个钩子函数watchEffect
- 默认会执行一次
- 返回值是一个函数,可以作为满足一定条件后停止回调函数
关于watch的参数上一篇只介绍了watch的第一个参数的第一种写法:getter函数
这一篇把侦听器watch收尾
说正文
一、第一个参数
1.1 getter
这个参数在上一篇中有详细的介绍,这里就不再叙述了和Vue3和解的Day8--侦听器watchEffect在组合式中
1.2 侦听一个可响应式对象
1.2.1 侦听reactive对象
我们知道reactive可以定义一个可响应式的对象,那么同响应式中一样,我们可以侦听整个对象或者侦听内部属性,那么这俩在组合式API中有什么区别呢?
在这里我先写一个基础的代码,下面基本代码就不重复写了,只写watch侦听的部分
<template>
<div>
<h2>{{ info.name }}</h2>
<button @click="changeInfo">changeInfo</button>
</div>
</template>
<script>
import { reactive, watch } from 'vue';
export default {
setup() {
const info = reactive({
name: "小白"
})
const changeInfo = () => info.name = "小刚"
return {
info,
changeInfo,
}
}
}
</script>
- 直接侦听info对象
watch(info, (newValue, oldValue) => {
console.log("newValue:", newValue, "oldValue:", oldValue);
})
从状态栏的输出结果我们发现,侦听之后的新值和旧值都是新值,在复习一遍这句话:watch侦听只会侦听数据本身的变化,不会侦听内部的改变;因为newValue和oldValue都是proxy:直接侦听reactive对象,拿到的newValue和oldValue都是reactive对象
-
如果我们想要侦听之后获取到的是一个普通的值,而不是reactive对象
我们需要知道一个前提reactive对象解构之后就会变成普通变量,所以我们可以对info进行解构,这里就需要借助getter这种写法了,
watch(() => ({...info}), (newValue, oldValue) => {
console.log("newValue:", newValue, "oldValue:", oldValue);
})
因为我这里写的是箭头函数,解构info的写法是{...info}和箭头函数结构重合,所以如果不想要写{}return这种形式,就需要在解构{...info}外套上一个()
或者直接写下面这种格式
watch(() => {
return {...info}
}, (newValue, oldValue) => {
console.log("newValue:", newValue, "oldValue:", oldValue);
})
解构之后获取的newValue和oldValue就是我们一开始想要的值了,这里留一个悬念,为什么解构之后得到的值就能侦听到我们想要的呢?
1.2.2 侦听ref对象
侦听ref对象直接侦听即可
<template>
<div>
<h2>{{ counter }}</h2>
<button @click="changeCounter">changeCounter</button>
</div>
</template>
<script>
import { watch, ref } from 'vue';
export default {
setup() {
const counter = ref(100)
const changeCounter = () => counter.value++
watch(counter, (newValue, oldValue) => {
console.log("newValue:", newValue, "oldValue:", oldValue);
})
return {
counter,
changeCounter
}
}
}
</script>
watch侦听ref对象没有什么需要说的,和选项式中直接侦听一个基本数据类型是一样的
1.2.3 侦听多个数据源
上面两个都是单独侦听reactive对象或者ref对象,如果我需要一个侦听器一起侦听一个reactive对象和一个ref对象呢?那就需要我们同时侦听多个数据源. 使用数组的形式接收多个数据源
<template>
<div>
<h2>{{ info.name }}</h2>
<h2>{{ counter }}</h2>
<button @click="changeClick">changeClick</button>
</div>
</template>
<script>
import { ref, reactive, watch } from 'vue';
export default {
setup() {
const counter = ref(100)
const info = reactive({ name: "小白" })
const changeClick = () => {
counter.value++
info.name = "小刚"
}
watch([counter, info], (newValue, oldValue) => {
console.log("newValue:", newValue, "oldValue:", oldValue);
})
return {
counter,
info,
changeClick
}
}
}
</script>
我们发现使用数组的形式侦听多个数据源之后获取到的结果也是一个数据形式,这里我们侦听reactive对象的时候还是侦听的整个对象,所以reactive对象的newValue和oldValue的值都是改变后的值。
我们可以对watch的第二个参数改成对应的数组形式,这样我们得到的结果就不是一个数组形式了(如下)
watch([counter, info], ([newCounter, newInfo], [oldCounter, oldInfo]) => {
console.log("newCounter:", newCounter, "newInfo:", newInfo,"oldCounter:", oldCounter,"oldInfo:", oldInfo,);
})
将第二个参数也变成数组接收值之后,获取到的值除了reactive对象的值都变成可一个一个普通值,之后我们依旧可以对reactive进行解构,让reactive对象侦听之后获取的值也是一个一个普通值。
watch([counter, () => ({ ...info })], ([newCounter, newInfo], [oldCounter, oldInfo]) => {
console.log("newCounter:", newCounter, "newInfo:", newInfo, "oldCounter:", oldCounter, "oldInfo:", oldInfo,);
})
同样,解构也有第二种写法
watch([counter, () => { return { ...info } }], ([newCounter, newInfo], [oldCounter, oldInfo]) => {
console.log("newCounter:", newCounter, "newInfo:", newInfo, "oldCounter:", oldCounter, "oldInfo:", oldInfo,);
})
如果reactive是一个数组形式,侦听数组并且解构
watch([counter, () => [...info]], ([newCounter, newInfo], [oldCounter, oldInfo]) => {
console.log("newCounter:", newCounter, "newInfo:", newInfo, "oldCounter:", oldCounter, "oldInfo:", oldInfo,);
})
二、 第三个参数:watch的选项
如果我们希望侦听一个深层的侦听,依旧需要将deep设置为true;也可以传入immediate立即执行
上面我留了一个悬念,就是reactive对象在未解构的时候侦听得到的值都是newValue, 解构之后得到的值就是想要的值了。因为reactive默认情况下侦听是深层的,但是我们解构之后使用时getter函数的形式,需要我们手动进行侦听
<template>
<div>
<h2>{{ info.name }}</h2>
<h2>{{ info.friends.name }}</h2>
<button @click="changeInfo">changeInfo</button>
</div>
</template>
<script>
import { reactive, watch } from 'vue';
export default {
setup() {
const info = reactive({
name: "小白",
friends: {
name: "abc"
}
})
const changeInfo = () => {
info.name = "小刚"
info.friends.name = "cba"
}
return {
info,
changeInfo
}
}
}
</script>
2.1 修改第一层数据
2.1.1 当我们针对整个info对象进行侦听
watch(info, (newValue, oldValue) => {
console.log("newValue:", newValue, "oldValue:", oldValue);
})
2.1.2 解构侦听
此时我们可以侦听到整体数据的改变,但是newValue和oldvalue都是同一个对象,所以输出的值是一样的
watch(() => ({...info}), (newValue, oldValue) => {
console.log("newValue:", newValue, "oldValue:", oldValue);
})
info.name和info.friend.name都进行修改的时候,直接侦听reactive对象解构写法也可以侦听。
2.2 只改变深层数据
const changeInfo = () => {
info.friends.name = "cba"
}
2.2.1 侦听整个info
watch(info, (newValue, oldValue) => {
console.log("newValue:", newValue, "oldValue:", oldValue);
})
此时深层数据改变,侦听整个对象还是能侦听到的,只不过newValue和oldValue是同一个对象
2.2.2 解构侦听
watch(() => ({...info}), (newValue, oldValue) => {
console.log("newValue:", newValue, "oldValue:", oldValue);
})
我们会发现,触发changeInfo方法,状态栏没有输出
当我们直接侦听多层嵌套对象的解构对象,会发现侦听根本不会执行, 这个时候就需要我们使用第三个参数中的deep:true属性了,手动进行深度侦听
watch(() => ({...info}), (newValue, oldValue) => {
console.log("newValue:", newValue, "oldValue:", oldValue);
}, {
deep: true,
})
reactive对象默认情况下是深度侦听的,但是我们侦听的是reactive对象进行解构,那么就需要我们手动进行侦听
2.3 immediate
watch的第三个选项,还有一个参数是immediate,整个参数是是否让侦听立即执行的,默认是false
三、setup语法糖中使用watch
其实watch在setup语法糖中和在setup函数中的写法是一致的,这个没有区别,这里我就不再重新写一遍了
说再见
关于watch在组合式中的写法就算全部完结了
难忘今宵
不能用手摸收银小票?
收银小票中含有双酚A,虽然双酚A不会致癌,但双酚A毕竟是一种有害物质,过量摄入对人体有害。