Vue3中watch和watchEffect的使用场景和区别

news/2025/2/9 4:11:57 标签: javascript, 前端, vue.js, watch, watchEffect

目录

watch

场景一:监听单个或多个特定数据的变化并执行副作用

场景二:监听多个数据源

watchEffect

场景一:自动追踪依赖并执行副作用

场景二:初始化时立即执行副作用

区别

监听方式

回调触发时机

响应式数据追踪方式

深度监听

减少watch中深度监听的性能开销的优化方法

精简被监听的数据结构

优化回调函数逻辑

精准监听


在 Vue 3 中,watchwatchEffect 都是用于响应式数据监听的方法,但它们的使用场景和工作方式不同。


watch


场景一:监听单个或多个特定数据的变化并执行副作用


当你需要精确地监听某个响应式数据(如 ref 或 reactive 中的某个属性)的变化,并在变化时执行一些特定的操作时,watch 非常有用。

例如,监听用户登录状态的变化,当登录状态改变时,更新页面的导航栏显示或者发起一些网络请求。

javascript">
<template>
  <div>
    <input v-model="searchQuery" placeholder="Search">
  </div>
</template>

<script lang="ts">
import { defineComponent, ref, watch } from 'vue';

export default defineComponent({
  setup() {
    const searchQuery = ref('');

    watch(searchQuery, (newValue, oldValue) => {
      console.log(`Search query changed from ${oldValue} to ${newValue}`);
      // 这里可以发起搜索请求
    });

    return {
      searchQuery
    };
  }
});
</script>

场景二:监听多个数据源


watch 也可以监听多个响应式数据的变化。例如,在一个购物车应用中,同时监听商品数量和商品价格的变化,来更新总价。

javascript">
<template>
  <div>
    <input v-model="quantity" type="number">
    <input v-model="price" type="number">
    <p>Total: {
  
  { totalPrice }}</p>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref, watch } from 'vue';

export default defineComponent({
  setup() {
    const quantity = ref(1);
    const price = ref(10);
    const totalPrice = ref(0);

    watch([quantity, price], ([newQuantity, newPrice], [oldQuantity, oldPrice]) => {
      totalPrice.value = newQuantity * newPrice;
    });

    return {
      quantity,
      price,
      totalPrice
    };
  }
});
</script>

watchEffect


场景一:自动追踪依赖并执行副作用


watchEffect 适合于当你有一个副作用函数,它依赖于多个响应式数据,并且你希望每当这些依赖中的任何一个发生变化时,该函数自动重新执行。

例如,在一个实时数据展示应用中,有多个数据来源影响某个图表的显示,使用 watchEffect 可以方便地实现图表的自动更新。

javascript"><template>
  <div>
    <input v-model="count" type="number">
    <input v-model="factor" type="number">
    <p>Result: {
  
  { result }}</p>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref, watchEffect } from 'vue';

export default defineComponent({
  setup() {
    const count = ref(1);
    const factor = ref(2);
    const result = ref(0);

    watchEffect(() => {
      result.value = count.value * factor.value;
    });

    return {
      count,
      factor,
      result
    };
  }
});
</script>

场景二:初始化时立即执行副作用


watchEffect 会在创建时立即执行一次回调函数,这在需要在组件初始化时就执行一些与响应式数据相关的操作时很有用。比如,初始化一个地图组件,其初始位置依赖于一些响应式的坐标数据。

区别

监听方式

  • watch:显式地指定要监听的数据源,可以是单个响应式数据(如 ref)或多个响应式数据组成的数组。它需要明确指出要监听什么。
  • watchEffect:自动追踪回调函数中使用的所有响应式数据作为依赖,不需要显式指定要监听的数据源。只要回调函数中使用的响应式数据发生变化,它就会重新执行。

回调触发时机

  • watch 的回调函数只有在被监听的数据源发生变化时才会触发。它会传入新值和旧值两个参数,方便你对比数据变化前后的状态。
  • watchEffect 的回调函数在组件初始化时会立即执行一次,以建立依赖关系。之后,每当依赖的响应式数据发生变化时,回调函数都会重新执行。

响应式数据追踪方式

  • watch 设置 deep: true 时主要针对手动指定的监听源进行深度对比。
  • watchEffect 是基于依赖追踪,只要在回调中使用到了响应式数据,无论层次多深,Vue 都会处理其变化通知。

深度监听

watch中深度监听:

  • 使用 deep 选项(针对 reactive 数据),可以通过设置 deep: true 选项来开启深度监听
  • 监听 ref 类型的对象或数组(深度监听),对于 ref 类型的对象或数组,watch 会自动进行深度监听,不需要额外设置 deep 选项

深度监听会消耗更多的性能,因为 Vue 需要递归地遍历对象的所有属性来检测变化。所以,建议只有在确实需要深度监听时才使用这种方式。

性能消耗原因:

  • 深度监听原理:当开启深度监听(对于 reactive 创建的数据通过设置 deep: true)时,Vue 需要递归地遍历被监听对象的所有属性,为每一个属性都设置响应式追踪。这意味着在对象属性较多且层级较深的情况下,会有大量的属性需要处理。随着对象结构的复杂程度增加,遍历和追踪的工作量呈指数级增长。
  • 频繁触发回调:深度监听会对对象内部的每一个属性变化都进行检测,只要任何一个深层属性发生变化,都会触发 watch 的回调函数。如果在短时间内有多个深层属性连续变化,回调函数可能会被频繁调用。频繁调用回调函数会带来额外的性能开销,特别是当回调函数内部有复杂的逻辑(如大量计算、DOM 操作、网络请求等)时,这种性能影响会更加明显。
  • 内存占用:深度监听会让 Vue 的响应式系统维护更多的内部状态,包括依赖关系的记录等。这会占用更多的内存空间,对于内存资源有限的应用场景(如在移动设备上运行的应用),可能会导致性能问题,甚至出现内存溢出的风险

减少watch中深度监听的性能开销的优化方法

精简被监听的数据结构

减少嵌套层级:尽量简化需要深度监听的对象结构,减少不必要的嵌套层级。例如,如果你的数据结构中有一些深层嵌套且很少变化的部分,可以考虑将其提取出来,不放在深度监听的对象范围内。
按需拆分对象:对于大型复杂对象,可以根据变化频率和业务逻辑,将其拆分成多个较小的对象。只对那些需要深度监听且变化频繁的部分进行深度监听,其他部分单独处理。


优化回调函数逻辑

避免复杂计算:确保 watch 回调函数内部不进行复杂的计算操作。如果有复杂计算需求,可以将计算逻辑提取到独立的函数中,并在需要时调用,而不是每次回调都执行。

javascript"><template>
  <div>
    <button @click="updateData">Update Data</button>
  </div>
</template>

<script lang="ts">
import { defineComponent, reactive, watch } from 'vue';

export default defineComponent({
  setup() {
    const data = reactive({
      nested: {
        value: 'initial'
      }
    });

    const complexCalculation = () => {
      // 复杂计算逻辑,例如大数据量的数组处理
      let result = 0;
      for (let i = 0; i < 1000000; i++) {
        result += i;
      }
      return result;
    };

    watch(
      () => data.nested,
      (newValue, oldValue) => {
        // 这里只执行必要的操作,避免复杂计算
        console.log('Nested value changed:', newValue, oldValue);
        // 如果确实需要复杂计算结果,在必要时调用
        const calculationResult = complexCalculation();
        console.log('Calculation result:', calculationResult);
      },
      {
        deep: true
      }
    );

    const updateData = () => {
      data.nested.value = 'updated';
    };

    return {
      data,
      updateData
    };
  }
});
</script>

防抖和节流

如果 watch 回调函数中执行的操作(如网络请求、DOM 操作)不希望被频繁触发,可以使用防抖或节流技术。可以使用 lodash 库中的 debounce 方法、 throttle 方法

javascript">
import { debounce } from 'lodash';

watch(
    () => data.nested,
    debounce((newValue, oldValue) => {
        // 这里执行网络请求或其他不希望频繁触发的操作
        console.log('Nested value changed (debounced):', newValue, oldValue);
    }, 300),
    {
        deep: true
    }
);
javascript">
import { throttle } from 'lodash';

watch(
    () => data.nested,
    throttle((newValue, oldValue) => {
        // 这里执行网络请求或其他不希望频繁触发的操作
        console.log('Nested value changed (throttled):', newValue, oldValue);
    }, 300),
    {
        deep: true
    }
);
精准监听

只监听关键属性,不要对整个对象进行深度监听,而是通过计算属性或方法,只监听对象中真正需要关注的关键属性。


若碰到其他的问题 可以私信我 一起探讨学习
如果对你有所帮助还请 点赞 收藏 谢谢~!
关注收藏博客 持续更新中


http://www.niftyadmin.cn/n/5845502.html

相关文章

c#中lock的经典示例

lock 是 C# 中的一种用于同步线程执行的机制&#xff0c;它帮助确保多个线程在访问共享资源时不会发生冲突或数据损坏。其作用是通过给临界区&#xff08;即多线程访问共享资源的代码段&#xff09;加锁&#xff0c;使得在同一时刻只能有一个线程进入执行该代码段。 1、lock 的…

第五十八章 Linux INPUT 子系统实验

imx6ull-alientek-emmc.dts &#xff08;使用第四十九章的设备树文件即可&#xff09; keyinput.c #include <linux/types.h> #include <linux/kernel.h> #include <linux/delay.h> #include <linux/ide.h> #include <linux/init.h> #include…

GitPuk快速安装配置教程(入门级)

GitPuk是一款国产开源免费的代码管理工具&#xff0c;工具简洁易用&#xff0c;开源免费&#xff0c;本文将讲解如何快速安装和配置GitPuk&#xff0c;以快速入门上手。 1、安装 支持 Windows、Mac、Linux、docker 等操作系统。 1.1 Linux安装&#xfeff; 以下以Centos7安装…

unity学习31:Video Player 视频播放相关基础

目录 1 新增Video Player的 component 2 导入视频到Asset里 3 拖入到 video player的 video clip里去即可 4 渲染模式 4.1 多种渲染模式 4.2 如果选择 Render Texture模式 4.3 然后把Render Texture 拖到游戏里的 gameObject上面 5 在UI上显示 5.1 创建UI 5.2 在UI上…

深入解析:Jsoup 库的多功能应用场景

Jsoup 是一个强大的 Java 库&#xff0c;主要用于解析和操作 HTML 文档。它不仅广泛应用于网络爬虫和数据抓取&#xff0c;还在网页内容分析、数据清洗与处理、自动化测试等多个领域有着广泛的应用。本文将详细介绍 Jsoup 库的多种用途&#xff0c;并提供具体的代码示例。 一、…

Fiddler Classic(HTTP流量代理+半汉化)

目录 一、关于Fiddler (一) Fiddler Classic (二) Fiddler Everywhere (三) Fiddler Everywhere Reporter (四) FiddlerCore (五) 总结 二、 软件安全性 1. 软件安装包 2. 软件汉化dll 三、安装与半汉化 1. 正常打开安装包点击下一步安装即可&#xff0c;安装路径自…

【提示词工程】探索大语言模型的参数设置:优化提示词交互的技巧

在与大语言模型(Large Language Model, LLM)进行交互时,提示词的设计和参数设置直接影响生成内容的质量和效果。无论是通过 API 调用还是直接使用模型,掌握模型的参数配置方法都至关重要。本文将为您详细解析常见的参数设置及其应用场景,帮助您更高效地利用大语言模型。 …

【重新认识C语言----结构体篇】

目录 -----------------------------------------begin------------------------------------- 引言 1. 结构体的基本概念 1.1 为什么需要结构体&#xff1f; 1.2 结构体的定义 2. 结构体变量的声明与初始化 2.1 声明结构体变量 2.2 初始化结构体变量 3. 结构体成员的访…