侦听器-watch和watchEffect使用
一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第2天,点击查看活动详情。
1.侦听器介绍
在Vue3中可以通过
watch
或者是watchEffect
来创建侦听器。watch
需要手动指定监听属性。而watchEffect
类型计算属性,它会追踪在回调中使用的属性进行监听。
1. watch基本使用
通过watch
来监听一个响应式数据,监听的属性的值只能是对象类型,不能监听基本类型的数据。
watch
接收三个参数: 1.需要监听的对象 2.侦听器回调函数 3. 配置对象。
watch
监听的属性的值可以是:响应式对象、计算属性、或多个来源组成的数组
const person: Person = reactive({
age: 18,
name: "person",
})
watch(person, () => {
console.log("person发送变化了");
})
//监听基本类型的数据会报错,大概意思是传入的值必须是object类型。
watch(person.age,()=>{
})
如果你想监听基本类型的响应式数据,需要借助于计算属性。通过计算属性,返回一个响应式的ref对象,然后就可以被监听。
const person: Person = reactive({
age: 18,
name: "person",
})
const name = computed(() => {
return person.name;
})
watch(name, () => {
console.log("监听到属性发生变化");
})
setTimeout(() => {
person.name = "tom"//修改数据,测试是否能监听
}, 3000)
计算属性在watch
中可以简写,直接传入一个getter
函数。
const name = computed(() => {
return person.name;
})
watch(name, () => {
console.log("监听到属性发生变化");
})
//上面等价于:
watch(() => person.name, () => {
console.log("监听到属性发生变化");
})
2. watch深层监听
watch
监听响应式对象,默认是深度监听。
const person: Person = reactive({
age: 18,
name: "person",
sex: {
sex: "女"
}
})
watch(person, () => {
console.log(person, "监听到变化了"); //会触发。
})
setTimeout(() => {
person.sex.sex="na"
}, 3000)
但是如果监听的是一个计算属性返回的对象,则不是深度监听。
const person: Person = reactive({
age: 18,
name: "person",
sex: {
sex: "男"
}
})
watch(() => person.sex, () => {
console.log("监听到属性发生变化");
})
setTimeout(() => {
person.sex.sex = "女"; //修改了值,但是侦听器不起作用。不是深度监听
}, 3000)
你可以显式地加上 deep
选项,强制转成深层侦听器:
watch(
() => person.sex,
() => {
console.log("监听到属性发生变化");
},
{deep: true} //手动开启深度监听
)
setTimeout(() => {
person.sex.sex = "女";//修改,可以被监听到。
}, 3000)
注意:深度侦听需要遍历被侦听对象中的所有嵌套的 属性,当用于大型数据结构时,开销很大。因此请只在必要时才使用它,并且要留意性能。(Vue官网)
简单来说:深度监听的开销很大,使用的时候注意性能。
3. watchEffect使用
watch()
仅在侦听数据变化时,才会执行回调。而
watchEffect
则会立即执行一次回调,就是说watchEffect
会先执行一次回调,然后去追踪在回调中使用过的响应式属性,对这些属性进行追踪监听,当他们发生变化,从新执行回调。
watchEffect(() => {
let x: number = person.age;
console.log('watchEffect配置的回调执行了')
})
setTimeout(() => {
person.age = 30;
}, 3000)
上面的例子会执行两次,在watchEffect
被调用的时候执行一次,在修改person.age
的值后再执行一次。
注意1:watchEffect
只有在回调中使用数据才会进行监听。修改属性是不被监听的。(即get
属性才会监听,set
属性不进行监听)
watchEffect(() => {
person.age = 20; //在 watchEffect 中修改一个响应式的属性,不被watchEffect追踪监听。
console.log('watchEffect配置的回调执行了')
})
setTimeout(() => {
person.age = 30;//修改了值,但是监听失败。
}, 3000)
注意2:watchEffect
仅会监听同步使用的属性,在异步中使用属性,不会被追踪监听。
watchEffect(() => {
setTimeout(() => {
console.log(person.age);
}, 2000)
console.log('watchEffect配置的回调执行了')
})
setTimeout(() => {
person.age = 30;//修改了值,但是监听失败。
}, 5000)
上面的例子:在异步中使用了 person.age
,然后再定时器中去修改person.age
的值,侦听器不起作用。
结论:watchEffect
只监听同步使用的属性。
注意3:watchEffect
是基于属性的追踪,如果监听的属性的值是一个对象,那么修改对象内部的属性,是不被监听到的。(watchEffect
不是深度监听)
const person: Person = reactive({
age: 18,
name: "person",
property: {
car: false
}
})
watchEffect(() => {
//监听property的值是一个对象。
console.log(person.property);
})
setTimeout(() => {
person.property.car = true;//修改对象里面的属性,不会被监听到。
}, 3000)
setTimeout(() => {
//修改property,重新指向一个对象,可以监听到。
person.property = {
car: false
}
}, 5000)
4. 侦听器回调执行的时机。
当我们修改了一个响应式属性的值,是侦听器先执行还是Vue先执行更新DOM?
在Vue的官网中有明确的说明:默认情况下,用户创建的侦听器回调,都会在 Vue 组件更新之前被调用。这意味着你在侦听器回调中访问的 DOM 将是被 Vue 更新之前的状态。
简单来说就是侦听器会在Vue更新DOM之前执行。
如果想在Vue更新后才执行侦听器的回调,可以使用watchPostEffect()
来创建监听。通过它创建的侦听器,就是更新DOM之后的回调。
watchPostEffect(() => {
console.log(person.age);
})
setTimeout(() => {
person.age = 30;
}, 5000)
5. watch和watchEffect的区别
- 通过
watch
侦听器需要指定监听的数据,并且监听的数组的值不能是一个基本类型的数据。如果想要监听一个基本数据类型的属性,需要通过计算属性。 - 而
watchEffect
创建侦听器,不需要指定监听的属性。并且在创建侦听器的时候会立即执行一次回调函数,并追踪在回调中使用过的响应式数据(get)。当数据发送变化,重新执行回调。 - 在
watch
监听一个对象,默认都是深度监听的,而watchEffect
则是基于属性的追踪监听,如果监听属性是一个对象,修改该对象的属性,是不会被监听到的。只有从新给整个属性赋值一个新的对象才会被监听到。
6. 总结
-
watch:
- 指定一个监听属性,可以是对象,计算属性或者数组。但不能是一个基本数据类型。
- 如果要监听一个基本的数据类型,需要通过计算属性。
- 默认都是深度监听的,但是如果监听的是一个计算属性,则不是深度监听。(可以为它手动开启深度监听)
-
watchEffect
-
watchEffect
创建的侦听器,会立即执行一次回调,并追踪在回调中使用的响应式属性,当追踪的响应式属性发送变更,从新执行侦听器回调。 -
只会监听在同步中使用的属性,在异步中使用的属性,不会被追踪。
-
watchEffect
是浅监听,如果监听的是一个对象,那么修改该对象的属性的值,不会被监听到,只有给属性重新赋值一个对象的时候,才会被监听到。
-