当前位置: 首页 > news >正文

vue 3:监听器

目录

1. 基本概念

2. 侦听数据源类型

1. 监听getter函数

2. 监听 ref 或 reactive 的引用

3. 多个来源组成的数组

4. 避免直接传递值!!!

3. 深层侦听器

4. 立即回调的侦听器

5. 一次性侦听器

6. watchEffect()

7. 暂停、恢复和停止侦听器

8. 清理副作用

1. watch

2. watchEffect


1. 基本概念

在 Vue.js 中,**监听器(watchers)**是一种用于监听数据变化并在变化发生时执行相应操作的功能。监听器通常用于执行异步操作或复杂的逻辑计算,例如:监控数据变化,发起 API 请求,执行深层次的对象或数组变化监听等。

<template><el-card><p>{{ num }}</p><input placeholder="Enter" type="text" v-model.lazy="num" /></el-card>
</template>
<script setup lang="ts">
import { ref, watch } from 'vue'const num = ref(1)
// watch接收两个参数(数据源,回调函数)
watch(num, (newVal, oldVal) => {console.log(newVal, oldVal)
})
// 回调函数接收两个参数(新值,旧值)
</script>

上面的例子中,当input发生change事件时,num的值value会改变,同时监听器会监听到num的变化,并触发回调函数


2. 侦听数据源类型

watch 的第一个参数( "数据源"),形式上可以是ref,computed,reactive,getter,props或响应式对象。但本质上 必须是一个 getter 函数。这个限制是为了确保 watch 能够正确地监听数据的变化,同时提供更精确和高效的响应式更新。

1. 监听getter函数

const num1 = ref(1)
const num2 = ref(1)
watch(() => num1.value + num2.value,(a, b) => {console.log(a, b)},
)

2. 监听 ref 或 reactive 的引用

如果监听的对象是 ref 或 reactive 创建的引用,可以直接传入。

const count = ref(0);
watch(count,  // 直接传递 ref 引用(newCount, oldCount) => {console.log(`Count changed from ${oldCount} to ${newCount}`);}
)

3. 多个来源组成的数组

watch([num1, () => num2.value], ([new1, new2], [old1, old2]) => {console.log(new1, new2, old1, old2)
})

4. 避免直接传递值!!!

如果传递的是非响应式的普通值,Vue 无法监听它的变化。例如,

const state = reactive({user: { name: 'Alice', age: 25 },
})watch(state.user.name, (newName, oldName) => {console.log(`User name changed from ${oldName} to ${newName}`)
})

控制台警告,并且并不能监听到 state.user.name的变化

传递 state.user.name 而不是 () => state.user.name ,因为此时 watch 直接接收了一个静态值,而不是可变的引用或 getter。

查了一下大概的原因:

因为watch底层逻辑依赖了proxy代理,而静态值不能被proxy代理的。

而之所以不用普通对象,而是用函数对象,则是和vue的响应式系统的依赖跟踪与收集有关。

 PS:这种坑很容易在props传值的时候踩

const props = defineProps({msg: {type: String,default: 'hello world',},
})
watch(() => props.msg,(newVal, oldVal) => {console.log(newVal, oldVal)},
)

3. 深层侦听器

官方:直接给 watch() 传入一个响应式对象,会隐式地创建一个深层侦听器——该回调函数在所有嵌套的变更时都会被触发

const count = reactive({num1: 0,num2: {a: 1,b: 2,},
})
watch(count, (newNum, oldNum) => {console.log(`Count changed from ${oldNum} to ${newNum}`)console.log(oldNum, newNum)
})

上面代码中,当num1或num2中的a、b发生改变时,打印的结果是

因为监听的数据源是一个对象,那么即使监听到对象持有值变了,新旧值也都是数据源对象的地址。与之相对,无论是对象的哪一个内容变化了,都相当于该对象变化了。

那如果我们监听num2呢

watch(count.num2, (newNum, oldNum) => {console.log(`Count changed from ${oldNum} to ${newNum}`)console.log(oldNum, newNum)
})

当然,同理,无论a还是b改变,都会被监听到。因为reactive也让num2具有了响应性

那如果我们使用getter来监听num2呢?

watch(() => count.num2,(newNum, oldNum) => {console.log(`Count changed from ${oldNum} to ${newNum}`)console.log(oldNum, newNum)},
)

那么无论改变a还是b,回调函数的都不会执行,因为getter返回的是num2对象的地址啊😂,地址不变,当然监听不到变化

当然,我们也可以这样,显式地加上 deep 选项,强制转成深层侦听器:

watch(() => count.num2,(newNum, oldNum) => {console.log(`Count changed from ${oldNum} to ${newNum}`)console.log(oldNum, newNum)},{ deep: true },
)

vue 3.5+中,我们还可以设置深度监听的层数,如{ deep: 1},来限制最大遍历深度——即 Vue 应该遍历对象嵌套属性的级数

const count = reactive({num1: 0,num2: {a: 1,b: 2,},
})
watch(count, (newNum, oldNum) => {console.log(oldNum, newNum)
}, {deep: 1
})
count.num2.a++

比如上面的例子中,当我们改变num2.a的值时,侦听回调函数就不会执行


4. 立即回调的侦听器

watch 默认是懒执行的:仅当数据源变化时,才会执行回调。也就是说在创建创建侦听器时,并不会执行。但但在某些场景中,我们又会有这种需求。

比如:我们想请求一些初始数据,然后在相关状态更改时重新请求数据。

watch(source,(newValue, oldValue) => {// 立即执行,且当 `source` 改变时再次执行},{ immediate: true }
)

5. 一次性侦听器

每当被侦听源发生变化时,侦听器的回调就会执行。但有时候会希望回调函数只在源变化时触发一次,那么就可以使用 once: true 选项。  注意:仅支持 vue 3.4 及以上版本 

watch(source,(newValue, oldValue) => {// 当 `source` 变化时,仅触发一次},{ once: true }
)

6. watchEffect()

watchEffect 会自动收集所有在回调函数内使用的响应式数据,并在其中任何一个数据发生变化时重新运行回调函数。适合在不需要明确指定数据源的情况下自动收集依赖的数据。比如:

下面的代码,在每当 todoId 的引用发生变化时使用侦听器来加载一个远程资源:

const todoId = ref(1)
const data = ref(null)watch(todoId,async () => {const response = await fetch(`https://jsonplaceholder.typicode.com/todos/${todoId.value}`)data.value = await response.json()},{ immediate: true }
)

注意:侦听器在这里两次使用了 todoId 的,一次是作为源,另一次是在回调中。

像这种情况,我们可以用 watchEffect 函数 来自动跟踪回调的响应式依赖。

watchEffect(async () => {const response = await fetch(`https://jsonplaceholder.typicode.com/todos/${todoId.value}`)data.value = await response.json()
})

回调会立即执行,且不需要指定 immediate: true。在执行期间,它还会自动追踪 todoId.value 作为依赖(和计算属性类似)。每当 todoId.value 变化时,回调会再次执行。

虽然对于这种依赖项只有一个得例子来说,watchEffect的作用不是很明显,但如果有许多依赖项,且数据源不明确时呢,炒鸡好用!!

所以:watch 和 watchEffect 的主要区别其实就是追踪响应式依赖的方式不同而已。


7. 暂停、恢复和停止侦听器

<template><el-card><p>num: {{ count.num1 }}</p><el-button @click="count.num1++">add</el-button><el-button @click="pause">暂停</el-button><el-button @click="resume">继续监听</el-button><el-button @click="stop">停止监听</el-button></el-card>
</template>
<script setup lang="ts">
import { reactive, watch } from 'vue'const count = reactive({num1: 1,
})const { stop, resume, pause } = watch(() => count.num1,(newNum, oldNum) => {console.log(oldNum, newNum)},
)
</script>

注意:仅支持 vue 3.5 及以上版本 

官方文档地址:响应式 API:核心 | Vue.js (vuejs.org)


8. 清理副作用

侦听器会在数据源变化时执行一些“副作用”。但这些“副作用”可能会产生一些需要后续清理的资源(如未完成的异步请求、事件监听器等),Vue提供了机制来清理这些资源,以避免内存泄漏或其他潜在问题。

1. watch

Vue 3.5之前,watch的第三个参数是一个可选的副作用清理函数。这个函数会在侦听器被停用或组件卸载时调用,允许你进行资源清理。

watch(id, async (newId, oldId, onCleanup) => {const { response, cancel } = doAsyncWork(newId)// 当 `id` 变化时,`cancel` 将被调用,// 取消之前的未完成的请求onCleanup(cancel)data.value = await response
})

Vue 3.5+

import { onWatcherCleanup } from 'vue'watch(id, async (newId) => {const { response, cancel } = doAsyncWork(newId)onWatcherCleanup(cancel)data.value = await response
})

2. watchEffect

Vue 3.5之前, watchEffect 的第一个参数为副作用清理函数.

watchEffect(async (onCleanup) => {const { response, cancel } = doAsyncWork(newId)// 如果 `id` 变化,则调用 `cancel`,// 如果之前的请求未完成,则取消该请求onCleanup(cancel)data.value = await response
})

Vue 3.5+

import { onWatcherCleanup } from 'vue'watchEffect(async () => {const { response, cancel } = doAsyncWork(newId)// 如果 `id` 变化,则调用 `cancel`,// 如果之前的请求未完成,则取消该请求onWatcherCleanup(cancel)data.value = await response
})

注意:onWatcherCleanup 仅在 Vue 3.5+ 中支持,并且必须在 watchEffect 效果函数或 watch 回调函数的同步执行期间调用:不能在异步函数的 await 语句之后调用它。


若有错误或描述不当的地方,烦请评论或私信指正,万分感谢 😃

如果我的文章对你有帮助,你的赞👍就是对我的最大支持^_^

相关文章:

vue 3:监听器

目录 1. 基本概念 2. 侦听数据源类型 1. 监听getter函数 2. 监听 ref 或 reactive 的引用 3. 多个来源组成的数组 4. 避免直接传递值&#xff01;&#xff01;&#xff01; 3. 深层侦听器 4. 立即回调的侦听器 5. 一次性侦听器 6. watchEffect() 7. 暂停、恢复和停止…...

Java学习路线:Maven(四)Maven常用命令

在IDEA的Maven模块中&#xff0c;可以看到每个项目都有一个生命周期 这些生命周期实际上是Maven的一些插件&#xff0c;每个插件都有各自的功能&#xff0c;而双击这些插件就可以执行命令 这些命令的功能如下&#xff1a; clean&#xff1a;清除整个 target文件夹&#xff0c…...

服务器数据恢复—分区结构被破坏的reiserfs文件系统数据恢复案例

服务器数据恢复环境&#xff1a; 一台服务器中有一组由4块SAS硬盘组建的RAID5阵列&#xff0c;上层安装linux操作系统统。分区结构&#xff1a;boot分区LVM卷swap分区&#xff08;按照顺序&#xff09;&#xff0c;LVM卷中划分了一个reiserfs文件系统作为根分区。 服务器故障…...

lua入门教程:type函数

在Lua中&#xff0c;type 函数是一个内置函数&#xff0c;用于返回给定值的类型。Lua 支持多种数据类型&#xff0c;包括 nil&#xff08;空值&#xff09;、boolean&#xff08;布尔值&#xff09;、number&#xff08;数字&#xff09;、string&#xff08;字符串&#xff09…...

Java图片转word

该方法可以控制一页是否只显示存放一张图片 第一步 <dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>5.2.3</version></dependency><dependency><groupId>org.apache…...

立体视觉的核心技术:视差计算与图像校正详解

立体视觉的核心技术&#xff1a;视差计算与图像校正详解 在立体视觉中&#xff0c;通过双目相机&#xff08;即左右两台相机&#xff09;的不同视角捕获的图像&#xff0c;结合几何关系&#xff0c;我们可以推算出场景中物体的深度。本文将深入讲解如何基于视差&#xff08;di…...

PaddleNLP的FAQ问答机器人

项目源码获取方式见文章末尾&#xff01; 600多个深度学习项目资料&#xff0c;快来加入社群一起学习吧。 《------往期经典推荐------》 项目名称 1.【DDRNet模型创新实现人像分割】 2.【卫星图像道路检测DeepLabV3Plus模型】 3.【GAN模型实现二次元头像生成】 4.【CNN模型实…...

2024年12月中国多场国际学术会议,EI检索录用!

2024年12月&#xff0c;多场国际学术会议将在中国多地召开&#xff0c;涵盖AI、机器人、大数据、网络安全、传感制造、环境工程、物联网等领域&#xff0c;促进学术交流&#xff0c;录用论文将EI检索&#xff0c;诚邀国内外专家参会。 第三届人工智能、人机交互和机器人国际学…...

日语学习的难易程度

日语学习的难易程度是一个相对主观的问题&#xff0c;它受到多种因素的影响&#xff0c;包括个人的语言学习能力、学习方法、学习时间、学习资源的可获得性以及个人对日语文化的兴趣和投入程度等。以下是对日语学习难易程度的一些分析&#xff1a; 优点与易学之处 文字系统&am…...

java-web-web后端知识小结

spring框架三大核心: IOC--控制反转 DI---依赖注入 AOP--面向切面编程 web开发技术小结 1.过滤器,JWT令牌 2.三层架构 IOC, DI AOP, 全局异常处理, 事务管理 mybatis 3.数据操作与存储 mysql 阿里云OSS(云存储) 各个技术的归属: 1.过滤器, cookie,session--javaWeb 2.JWT, 阿里…...

常见的排序算法(二)

归并排序 归并排序&#xff08;Merge Sort&#xff09;是一种基于分治法&#xff08;Divide and Conquer&#xff09;的排序算法。它将一个大的问题分解成小的问题&#xff0c;然后递归地解决这些小问题&#xff0c;最后合并&#xff08;merge&#xff09;得到最终的排序结果。…...

spark的RDD分区的设定规则

目录 一、第一种&#xff1a;parallelize 获取rdd时 二、第二种&#xff1a;通过外部读取数据-textFile 三、上面提到了默认分区数&#xff0c;那么默认分区是怎么计算呢&#xff1f; 一、第一种&#xff1a;parallelize 获取rdd时 没有指定&#xff1a;spark.default.paral…...

【点云网络】voxelnet 和 pointpillar

VoxelNet 和 pointpillar 这两个网络可以认为后者是前者的升级版本&#xff0c;都是采用了空间划分的方法&#xff0c; 一个是体素&#xff0c;一个是pillar, 前者是3D卷积处理中间特征&#xff0c;后者是2D卷积处理中间特征。 voxelnet voxelnet 应该是比较早的onestage的网…...

HAL库硬件IIC驱动气压传感器BMP180

环境 1、keilMDK 5.38 2、STM32CUBEMX 初始配置 默认即可。 程序 1、头文件 #ifndef __BMP_180_H #define __BMP_180_H#include "main.h"typedef struct {float fTemp; /*温度&#xff0c;摄氏度*/float fPressure; /*压力&#xff0c;pa*/float fAltitude; /*…...

探索Python音频处理的奥秘:Pydub库的魔法

文章目录 探索Python音频处理的奥秘&#xff1a;Pydub库的魔法第一部分&#xff1a;背景介绍第二部分&#xff1a;Pydub是什么&#xff1f;第三部分&#xff1a;如何安装Pydub&#xff1f;第四部分&#xff1a;Pydub的简单函数使用方法1. 打开音频文件2. 播放音频3. 导出音频文…...

LeetCode 热题100(七)【链表】(2)

目录 7.6合并两个有序链表&#xff08;简单&#xff09; 7.7两数相加&#xff08;中等&#xff09; 7.8删除链表的倒数第N个节点&#xff08;中等&#xff09; 7.9两两交换链表中的节点&#xff08;中等&#xff09; 7.10k个一组翻转链表&#xff08;困难&#xff09; 7.6…...

计算机网络 TCP/IP体系 网络层

一. 网络层的基本概念 网络层主要负责将数据从源端主机发送到目的端主机。在这一过程中&#xff0c;网络层要解决的关键问题是数据包的路由选择&#xff0c;即确定数据包通过互联网的最佳路径。 1.1 网络层的信息类型 数据包&#xff1a;这是网络层传输的主要形式&#xff0c…...

迈入国际舞台,AORO M8防爆手机获国际IECEx、欧盟ATEX防爆认证

近日&#xff0c;深圳市遨游通讯设备有限公司&#xff08;以下简称“遨游通讯”&#xff09;旗下5G防爆手机——AORO M8&#xff0c;通过了CSA集团的严格测试和评估&#xff0c;荣获国际IECEx及欧盟ATEX防爆认证证书。2024年11月5日&#xff0c;CSA集团和遨游通讯双方领导在遨游…...

实习作假:阿里健康实习做了RABC中台,还优化了短信发送流程

最近有二本同学说&#xff1a;“大拿老师&#xff0c;能帮忙看下简历吗&#xff1f;” 如果是从面试官的角度来看&#xff0c;这个同学的实习简历是很虚假的。 但是我们一直强调的是&#xff1a;校招的实习简历是不能出现明显的虚假。 首先&#xff0c;你去公司做事情&#…...

Unity中IK动画与布偶死亡动画切换的实现

在Unity游戏开发中&#xff0c;Inverse Kinematics&#xff08;IK&#xff09;是创建逼真角色动画的强大工具。同时&#xff0c;能够在适当的时候切换到布偶物理状态来实现死亡动画等效果&#xff0c;可以极大地增强游戏的视觉体验。本文将详细介绍如何在Unity中利用IK实现常规…...

java导出word文件(手绘)

文章目录 代码细节效果图参考资料 代码细节 使用的hutool的WordUtil&#xff0c;WordUtil对poi进行封装&#xff0c;但是这一块的官方封装的很少&#xff0c;很多细节都没有。代码中是常见的绘制段落&#xff0c;标题、表格等常用api Word07Writer writer WordUtil.getWriter(…...

ssm070基于SSM框架的校园代购服务订单管理系统的设计与实现+vue(论文+源码)_kaic

毕业设计 题 目&#xff1a; 校园代购服务订单管理系统 作 者&#xff1a; 学 号&#xff1a; 所属学院&#xff1a; 专业年级&#xff1a; 学校导师&#xff1a; 职 称&#xff1a; 班级导师&#xff1a; 职 称&#xff1a; 完成时间…...

Java项目实战II基于Spring Boot的秒杀系统设计与实现(开发文档+数据库+源码)

目录 一、前言 二、技术介绍 三、系统实现 四、文档参考 五、核心代码 六、源码获取 全栈码农以及毕业设计实战开发&#xff0c;CSDN平台Java领域新星创作者&#xff0c;专注于大学生项目实战开发、讲解和毕业答疑辅导。 一、前言 在互联网电商蓬勃发展的今天&#xff0…...

FastAPI —— 请求参数验证

1.hello world 给后端船数据 hello world 接口给后端传 COVID-19 感染数据_高性能 FastAPI 框架入门精讲-慕课网 #!/usr/bin/python3 # -*- coding:utf-8 -*- # __author__ __Jack__from typing import Optionalfrom fastapi import FastAPI from pydantic import BaseModel…...

第七篇: BigQuery中的复杂SQL查询

BigQuery中的复杂SQL查询 背景与目标 在数据分析中&#xff0c;我们通常需要从多个数据源中获取信息&#xff0c;以便进行深入的分析。这时&#xff0c;BigQuery提供的JOIN、UNION和子查询等复杂SQL语句非常实用。本文将以Google BigQuery的公共数据集为例&#xff0c;介绍如何…...

【SQL实验】高级查询(难点.三)含附加数据库操作

完整代码在文章末尾【代码是自己的解答&#xff0c;并非标准答案&#xff0c;也有可能写错&#xff0c;文中可能会有不准确或待完善之处&#xff0c;恳请各位读者不吝批评指正&#xff0c;共同促进学习交流】 将素材中的“学生管理”数据库附加到SQL SERVER中&#xff0c;完成以…...

qt QFileSystemModel详解

1、概述 QFileSystemModel是Qt框架中的一个关键类&#xff0c;它继承自QAbstractItemModel&#xff0c;专门用于在Qt应用程序中展示文件系统的数据。这个模型提供了一个方便的接口&#xff0c;使得开发者可以轻松地在应用程序中集成文件和目录的树形结构&#xff0c;并通过视图…...

element plus中修改el-table的样式

文章目录 前情提要相关环境package.jsonvue代码结果 方式一直接看代码 方式二直接看代码 前情提要 因为项目中用到el-table的时候&#xff0c;需要将el-table表格的样式进行修改&#xff0c;将整个表格的背景颜色从白色变成透明&#xff0c;使得表格变得透明之后&#xff0c;展…...

深入理解封装与接口:Java程序设计的核心思想与最佳实践

目录 一、封装的优点 二、接口与默认方法 三、总结 在面向对象编程&#xff08;OOP&#xff09;中&#xff0c;封装&#xff08;Encapsulation&#xff09;是一个核心概念&#xff0c;Java对其进行了良好的支持。封装不仅有助于提高代码的安全性&#xff0c;还能够增强代码的…...

linux 下调试 mpu6050 三轴加速度

供自己备忘&#xff1b; 1. 参考资料&#xff1a; b 站视频 https://www.bilibili.com/video/BV1cL4y1x7FA/?spm_id_from333.337.search-card.all.click&vd_sourced7a07b8689c9e646f0214227c06f304c csdn 其它博客 https://blog.csdn.net/qq_65198598/article/detail…...