# Vue3教程 - 7 侦听器watch
和 Vue2 不同,Vue2 中是选项式API,Vue3 中是组合式API。
在 Vue 中,watch
函数是一个非常强大的工具,它允许我们监视数据的变化,并在数据发生变化时执行特定的操作。与 Vue2 中的 watch
相比,Vue3 的 watch
API 提供了更灵活和强大的功能,包括支持侦听多个数据源、深度侦听以及侦听 getter 函数等。
Vue3 中的 watch
可以监视多种类型的数据,主要有:
ref定义的数据
reactive定义的数据
函数的返回值(getter函数)
包含上述内容的数组
下面分别介绍一下几种常用的使用场景。
# 7.1 监视ref定义的基本类型数据
下面定义了基本数据类型的 sum
,然后点击按钮,将 sum
加 1,然后使用 watch
监听 sum
的值变化。
<template>
结果为:{{ sum }} <br/>
<button @click="plusOne">加1</button>
</template>
<!-- setup -->
<script lang="ts" setup>
import { ref, watch } from 'vue' // 1.引入 watch
let sum = ref(0)
function plusOne() {
sum.value++
}
// 监视sum的值的变化
watch(sum, (newValue, oldValue)=> {
console.log("newValue:", newValue, "oldValue:", oldValue)
})
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
首先引入 watch
函数,watch
函数第一个参数是监听的变量,第二个参数是一个回调函数,参数是新值和旧值 。
当点击按钮的时候,sum的值累加,控制台打印了 watch
的回调函数中的执行结果。
如何取消监视呢?
watch()
返回的是一个停止函数,调用返回的函数就可以取消监视了。
// 监视sum的值的变化
const stopWatch = watch(sum, (newValue, oldValue)=> {
console.log("newValue:", newValue, "oldValue:", oldValue)
// newValue大于等于5的时候,取消监视
if (newValue >= 5) {
stopWatch()
}
})
2
3
4
5
6
7
8
9
# 7.2 监视ref定义的对象类型数据
首先使用 ref 定义一个对象类型的响应式数据,然后使用按钮触发修改这个对象的属性和修改整个对象:
<template>
<div>{{ person.name }}</div>
<button @click="changeName">修改name</button>
<button @click="changePerson">修改person</button>
</template>
<!-- setup -->
<script lang="ts" setup>
import { ref, watch } from 'vue' // 1.引入 watch
let person = ref({"name": "doubi", "age": 13})
// 修改name
function changeName() {
person.value.name = "niubi"
}
// 修改person
function changePerson() {
person.value = {"name": "niubi", "age": 15}
}
// 监视person的值的变化
watch(person, (newValue, oldValue)=> {
console.log("newValue:", newValue, "oldValue:", oldValue)
})
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
运行的结果发现,修改对象的时候,可以触发回调, newValue
是新的对象, oldValue
是旧的对象。而当修改对象的属性的时候,是无法触发 watch
回调的。
默认情况下,watch
监视的是对象的地址是否发生变化,所以如果想监听属性的值是否发生变化,需要手动开启深度监视。
需要给 watch
第三个参数,第三个参数是一个配置对象,可以对 watch
进行一些配置。
此时就可以监听属性值的变化了:
// 监视person的值的变化,并开启深度监视
watch(person, (newValue, oldValue)=> {
console.log("newValue:", newValue, "oldValue:", oldValue)
}, {"deep":true})
2
3
4
但是需要注意,此时监视数据的变化,你会发现 newValue
和 oldValue
是一样的,因为此时对象没有变化,他们指向的是同一个 person
对象。
也就是说:
如果修改的是整个对象,newValue是新值,oldValue是旧值,因为不是同一个对象了;
如果修改的是对象中的属性,newValue和oldValue都是新值,因为它们是同一个对象;
另外,第三个参数不止 deep
一个参数,还有一些其他的配置,例如 immediate
等等:
// 监视person的值的变化
watch(person, (newValue, oldValue)=> {
console.log("newValue:", newValue, "oldValue:", oldValue)
}, {deep:true, immediate:true})
2
3
4
immediate
表示会立即触发一次,也就是进入页面就会触发一次监视的回调函数,此时 newValue
是初始化值,oldValue
是undefined
。
# 7.3 监视reactive定义的对象类型数据
reactive
只能定义对象类型的数据,在使用 watch 监视 reactive 定义的对象类型的数据的时候,默认是开启深度监视的,且无法关闭深度监视。
举个栗子:
<template>
<div>{{ person.name }}</div>
<button @click="changeName">修改name</button>
<button @click="changePerson">修改person</button>
</template>
<!-- setup -->
<script lang="ts" setup>
import { reactive, watch } from 'vue' // 1.引入 watch
// 创建响应式数据
let person = reactive({"name": "doubi", "age": 13})
// 修改name
function changeName() {
person.name = "niubi"
}
// 修改person
function changePerson() {
// 前面讲了,不能直接修改person,需要使用Object.assign进行赋值
Object.assign(person, {"name": "niubi", "age": 15})
}
// 监视person的值的变化
watch(person, (newValue, oldValue)=> {
console.log("newValue:", newValue, "oldValue:", oldValue)
})
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
在前面的章节讲了,不能直接将新的对象赋值给 person,需要使用 Object.assign
将一个对象的各个属性的值赋值给 person 对象的属性。
然后使用 watch 监听 person 数据的变化,因为对象的地址没有变化,所以 newValue 和 oldValue 是同一个对象,所以他们的值是一样的。
运行结果:
# 7.4 监视对象的属性
监视 ref
或 reactive
定义的对象类型的数据的属性。
监视的时候,属性分为两种情况,一种是基本数据类型的属性,一种是对象类型的属性。
为了演示,定义 person 对象,添加基本类型的属性和对象类型的属性。
如下:
<template>
<div>{{ person.name }}</div>
<div>{{ person.girlfriends.first }},{{ person.girlfriends.second }}</div>
<button @click="changeName">修改基本类型属性</button>
<button @click="changeFirstGirlFriend">修改对象属性</button>
<button @click="changeGirlFriends">改变整个对象属性</button>
</template>
<!-- setup -->
<script lang="ts" setup>
import { reactive, watch } from 'vue' // 1.引入 watch
// 定义响应式数据
let person = reactive({
name: "doubi",
girlfriends: { // 定义女朋友们
first: "赵今麦",
second: "李沁"
}
})
// 修改name
function changeName() {
person.name = "niubi"
}
// 修改第一个女朋友
function changeFirstGirlFriend() {
person.girlfriends.first = "高圆圆"
}
// 修改所有的女朋友
function changeGirlFriends() {
person.girlfriends = {first: "战鹰", second: "刘亦菲"}
}
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
下面来监听对象的属性。
# 1 监听基本数据类型的属性
watch
不能直接监听 person.name
,在前面说的 watch 能监听 函数的返回值(getter函数)
,所以这里需要将 person.name 通过一个函数返回。
// 监视person.name值的变化
watch(() => { return person.name }, (newValue, oldValue)=> {
console.log("newValue:", newValue, "oldValue:", oldValue)
})
2
3
4
箭头函数可以进一步简写:
watch(() => person.name , (newValue, oldValue)=> {
console.log("newValue:", newValue, "oldValue:", oldValue)
})
2
3
运行效果:
# 2 监视对象类型的属性
监视对象类型的属性,可以直接进行监视:
// 监视person.girlfriends值的变化,直接监视
watch(person.girlfriends , (newValue, oldValue)=> {
console.log("newValue:", newValue, "oldValue:", oldValue)
})
2
3
4
但是此时只能监视到对象类型属性值的变化,而无法监视到整个属性对象的替换(地址变化),也就是无法监视到 person.girlfriends = {first: "战鹰", second: "刘亦菲"}
,如果要监视对象变化,还是需要写成函数的形式:
// 使用函数的方式监听
watch(() => person.girlfriends , (newValue, oldValue)=> {
console.log("newValue:", newValue, "oldValue:", oldValue)
}, {deep: true})
2
3
4
但是需要注意,最后需要添加 {deep: true}
,否则只能监视对象属性整体的变化,无法监视到对象属性中单个属性的变化。
总结:
若属性不是对象类型,需要写成函数形式;
如果属性是对象类型,可直接监视,但是推荐写成函数形式,如果想进行深度监视,可以使用
{deep: true}
。
# 7.5 监视多个数据
如果要监视多个数据,只需要将要监视的数据放在一个数组中就可以了,当然要监视的数据是上面支持被监视的类型。
在上面监视对象的属性的例子上进行修改,监视 person.name
和 person.girlfriends.second
:
// 将要监视的数据放到数组中即可
watch([() => person.name, () => person.girlfriends.second], (newValue, oldValue)=> {
console.log("newValue:", newValue, "oldValue:", oldValue)
}, {deep: true})
2
3
4
回调函数的 newValue
和 oldValue
是整个数组。