监听器

利用监听器,可以在【响应式】的基础上添加一些副作用,把更多的东西变成【响应式的】

  • 原本只是数据变化 => 页面更新

  • watch 可以在数据变化时 => 其它更新

<template>
  <input type="text" v-model="name" />
</template>

<script setup lang="ts">
import { ref, watch } from "vue";
function useStorage(name: string) {
  const data = ref(sessionStorage.getItem(name) ?? "");
  watch(data, (newValue) => {
    sessionStorage.setItem(name, newValue);
  });
  return data;
}
const name = useStorage("name");
</script>
  • 名称为 useXXXX 的函数,作用是返回带扩展功能的【响应式】数据
  • localStorage 即使浏览器关闭,数据还在
  • sessionStorage 数据工作在浏览器活动期间

此外,参数中,新值和旧值都可以设为一个相对应的数组参数同时监听多个属性和对象

深度监听

监听器还有深度监听,可以深度监听对象中某个属性的变化进而更细致的操作,同时可以在watch内嵌套达到监听外层同时吗满足内层变化的时候执行不同的逻辑。

默认情况下,ref不会实现深度监听无法监听到对象例如数组中某个值的属性的具体变化,但reactive在源码中默认实现了deep属性的开启了

如果是ref,那么就需要传递第三个参数option,其中设置deep属性为开启

例子:通过监听对象的变化来实现计数器的功能

<template>
  <div>
    <h2>计数器</h2>
    <button @click="counter.value++">增加</button>
    <button @click="counter.value--">减少</button>
    <p>计数器的值为: {{ counter.value }}</p>
  </div>
</template>

<script>
import { ref, watch } from 'vue';

export default {
  setup() {
    const counter = ref(0);

    // 深度监听counter对象的变化
    watch(counter, (newValue, oldValue) => {
      console.log(`计数器的值从 ${oldValue} 变为了 ${newValue}`);
    }, { deep: true });

    return {
      counter
    }
  }
}
</script>

在上面的代码中,我们使用了Vue3的组合式API来定义一个计数器组件。在setup()函数中,我们首先使用ref()函数创建了一个响应式的计数器变量counter,并将其初始值设为0。

接着,我们使用watch()函数来对counter变量进行深度监听。当counter对象的任何属性发生变化时,回调函数就会被触发。在回调函数中,我们输出了计数器的新旧值。

在模板中,我们通过按钮来增加或减少计数器的值,并将计数器的值显示在页面上。

需要注意的是,由于深度监听可能会影响性能,因此应该尽可能避免在大型对象上使用深度监听。如果只需要监听对象的某些属性,可以考虑使用浅监听或计算属性来代替深度监听。

此外,如果我们只想要监听对象中某一个单一属性,可以将其参数传入为一个回调函数

image-20230331233234295

watchEffect高级侦听器

watch函数和watchEffect函数都是Vue3提供的侦听器函数,它们的区别在于watch需要显式指定要侦听的数据,而watchEffect会自动侦听函数内部使用的所有响应式数据。因此,在不同的场景下,我们可以选择使用不同的侦听器函数。

在同一个组件中,如果您需要同时侦听多个响应式数据的变化,可以使用多个watch函数或一个watchEffect函数。例如,以下代码展示了一个组件中同时使用watchwatchEffect的示例:

<template>
  <div>
    <h2>用户信息</h2>
    <p>姓名:{{ userInfo.name }}</p>
    <p>年龄:{{ userInfo.age }}</p>
    <p>性别:{{ userInfo.gender }}</p>
  </div>
</template>

<script>
import { reactive, watch, watchEffect } from 'vue';

export default {
  setup() {
    const userInfo = reactive({
      name: '张三',
      age: 18,
      gender: '男',
    });

    // 使用watch函数侦听name和age的变化
    watch(
      [() => userInfo.name, () => userInfo.age],
      ([newName, newAge], [oldName, oldAge]) => {
        console.log(`姓名或年龄发生变化:${oldName}(${oldAge}) => ${newName}(${newAge})`);
      }
    );

    // 使用watchEffect函数侦听userInfo的变化
    watchEffect(() => {
      console.log(`用户信息发生变化:${userInfo.name}(${userInfo.age}, ${userInfo.gender})`);
    });

    return {
      userInfo,
    };
  },
};
</script>

在上面的例子中,我们使用reactive函数创建了一个响应式对象userInfo,其中包含用户的姓名、年龄和性别。我们使用watch函数来侦听姓名和年龄的变化,使用watchEffect函数来侦听整个用户信息对象的变化。

当姓名或年龄发生变化时,watch函数的回调函数会被触发,输出变化前后的姓名和年龄。当用户信息对象发生变化时,watchEffect函数的回调函数会被触发,输出变化后的用户信息。

除了自动追踪函数内部的响应式数据外,watchEffect函数还有一些特别的属性和方法特性,可以帮助我们更好地使用和管理侦听器。

以下是watchEffect函数的一些特别属性和方法:

  1. stop: watchEffect函数返回一个函数,该函数可以用于停止侦听器。调用该函数后,侦听器将被销毁,不再侦听响应式数据的变化。例如:

    const stop = watchEffect(() => {
      // 函数体
    });
    stop(); // 停止侦听
    
  2. onInvalidate: watchEffect函数可以接收一个回调函数作为参数,在侦听器被销毁时触发。例如:

    const stop = watchEffect((onInvalidate) => {
      onInvalidate(() => {
        console.log('侦听器被销毁了');
      });
      // 函数体
    });
    stop(); // 停止侦听,触发onInvalidate回调函数
    

    在上面的例子中,我们在watchEffect函数的回调函数中调用了onInvalidate函数,并传入一个回调函数。当侦听器被销毁时,该回调函数会被触发,输出一条消息。

  3. scheduler: watchEffect函数可以接收一个scheduler函数作为参数,用于控制侦听器的执行时机。例如:

    const stop = watchEffect(
      () => {
        // 函数体
      },
      {
        scheduler: (job) => {
          console.log('调度器被触发了');
          job();
        },
      }
    );
    

    在上面的例子中,我们在watchEffect函数的第二个参数中传入了一个scheduler函数。该函数会在侦听器需要重新执行时被触发,我们可以在该函数内部实现自定义的调度逻辑。

需要注意的是,虽然watchEffect函数提供了一些特别的属性和方法,但在大多数情况下,我们不需要使用这些特性。在实际开发中,我们可以根据具体的需求来选择合适的侦听器函数和侦听器选项,以便更好地管理侦听器。(损耗性能)