Vue3-侦听器-watch和watchEffect的使用

2,904 阅读6分钟

侦听器-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是浅监听,如果监听的是一个对象,那么修改该对象的属性的值,不会被监听到,只有给属性重新赋值一个对象的时候,才会被监听到。