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 语句之后调用它。


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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/4149.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

c 语言链表的简单使用

一、链表介绍 在 C 语言中&#xff0c;链表是一种常用的数据结构&#xff0c;用于动态地存储数据。链表中的每个元素称为节点&#xff0c;每个节点包含数据部分和指向下一个节点的指针。 1.1 链表的基本概念 定义&#xff1a;链表是一种物理存储单元上非连续、非顺序的存储结…

计算机网络——路由器构成

算路由表是分布式去算——你算你的&#xff0c;我算我的 输出队列非先来先传 调度发生在哪里 缓存队列一般是应对——来数据方向的速度过快问题

微信小程序uniapp基于Android的流浪动物管理系统 70c3u

文章目录 项目介绍具体实现截图技术介绍mvc设计模式小程序框架以及目录结构介绍错误处理和异常处理java类核心代码部分展示详细视频演示源码获取 项目介绍 以往流浪猫狗的救助网站相关信息的管理&#xff0c;都是工作人员手工统计。这种方式不但时效性低&#xff0c;而且需要查…

【Pikachu靶场:XSS系列】xss之过滤,xss之htmlspecialchars,xss之herf输出,xss之js输出通关啦

一、xss之过滤 <svg onloadalert("过关啦")> 二、xss之htmlspecialchars javascript:alert(123) 原理&#xff1a;输入测试文本为herf的属性值和内容值&#xff0c;所以转换思路直接变为js代码OK了 三、xss之href输出 JavaScript:alert(假客套) 原理&#x…

【$15000】 通过监控调试模式实现RCE

你有没有遇到过一个你直觉上知道存在漏洞的端点&#xff0c;但你却无法完全理解后端发生了什么或如何利用它&#xff1f;在这篇文章中&#xff0c;我将引导你了解一种技术&#xff0c;它将我的黑盒测试转变为半白盒测试。这种方法导致了多个漏洞的发现&#xff0c;并最终实现了…

npm镜像的常用操作

查看当前配置的 npm 镜像 npm config get registry切换官方镜像 npm config set registry https://registry.npmjs.org/切换淘宝镜像(推荐) npm config set registry https://registry.npmmirror.com/切换腾讯云镜像 npm config set registry http://mirrors.cloud.tencent…

Fake Location解除屏蔽分析

前言:对于Fake Location的appconfigs.xml文件屏蔽分析 <?xml version1.0 encodingutf-8 standaloneyes ?> <map><string name"config">{&quot;disabledApps&quot;:[&quot;com.srit.swork.views&quot;,&quot;com.sqjz&q…

3D系统开发工具HOOPS SDK如何实现PLM应用的创新与优化?

无论是支持汽车、航空航天、医疗设备还是建筑&#xff0c;产品生命周期管理(PLM)解决方案实际上都是将制造生产系统结合在一起的粘合剂&#xff0c;从头到尾提供数字线程并为最终用户优化流程。 Tech Soft 3D在行业内近30年&#xff0c;我们对领先的应用程序所基于的组件技术&…

数据结构和算法之树形结构B+树(7)

前一章节我们介绍了B树&#xff0c;了解了B树是适用于大规模数据存储和磁盘访问‌的树结构&#xff0c;而今天要讲的B是B树的一种改进&#xff0c;是B树的一种优化和改进&#xff0c;被大多数据库系统采纳作为索引结构使用。 一、基本概念 B树是B树的改进&#xff0c;因…

用 Python 爬取淘宝商品价格信息时需要注意什么?

用 Python 爬取淘宝商品价格信息时&#xff0c;需要注意以下方面&#xff1a; 一、法律和道德规范&#xff1a; 遵守法律法规&#xff1a;网络爬虫的行为应在法律允许的范围内进行。未经淘宝平台授权&#xff0c;大规模地爬取其商品价格信息并用于商业盈利等不当用途是违法的…

基于Python的自然语言处理系列(50):Soft Prompt 实现

在本篇文章中,我们将实现一个简单的 Soft Prompt 技术,该技术允许我们仅微调新增的嵌入权重,而保持预训练模型不变。Soft Prompt 的主要优势在于它的参数高效性,使得模型在特定任务上快速适应,而无需重新训练模型的所有权重。 1. Soft Prompt 概述 Soft Prompt 技术来源于…

stack和queue --->容器适配器

不支持迭代器&#xff0c;迭代器无法满足他们的性质 边出边判断 实现 #define _CRT_SECURE_NO_WARNINGS 1 #include<iostream> #include<stack> #include<queue> using namespace std; int main() {stack<int> st;st.push(1);st.push(2);st.push(3);…

UE5 材质篇 1 如何偏移顶点

顶点偏移 start content里的plane长这样 我们进行一点顶点偏移就能长这样 XY加起来乘个缩放系数扔给sin结果乘个缩放系数即可

MySQL45讲 第十六讲 “order by”是怎么工作的?

文章目录 MySQL45讲 第十六讲 “order by”是怎么工作的&#xff1f;一、引言二、全字段排序&#xff08;一&#xff09;索引创建与执行情况分析&#xff08;二&#xff09;执行流程&#xff08;三&#xff09;查看是否使用临时文件 三、rowid 排序&#xff08;一&#xff09;参…

Ansys HFSS:外壳的屏蔽效果演示

欢迎回来&#xff01;随着电子系统变得越来越复杂和集成&#xff0c;确保适当的屏蔽以减轻电磁干扰 &#xff08;EMI&#xff09; 变得越来越重要。 继续讨论屏蔽效果&#xff0c;我们现在将重点转移到另一个强大的工具上&#xff1a;Ansys HFSS&#xff08;高频结构仿真器&am…

无人机避障——(局部规划方法)DWA(动态窗口法)

传统的DWA算法更加倾向于车辆等差速无人车&#xff0c;旋翼无人机是全速的&#xff0c;全向的。 全局路径是通过A*算法生成的 局部路径规划效果&#xff1a; DWA算法效果&#xff1a; 过程图&#xff1a; 完整过程&#xff1a; PID算法效果&#xff1a; 过程图&#xff1a…

数据库->视图

目录 一、视图 1.什么是视图 ​编辑 2.创建视图 1.语法 3.使用视图 4.视图的功能 1.屏蔽相关字段 2.对外提供统一访问规范 3.视图和真实表进行表连接查询 5.修改数据 6.注意事项 7.删除视图 1.语法 8.视图的优点 1. 简单性 2. 安全性 3. 逻辑数据独⽴性 4. 重…

el-scrollbar 动态更新内容 鼠标滚轮无效

有以下功能逻辑&#xff0c;实现了一个时间轴组件&#xff0c;点击、-号后像地图那样放大组件以显示不同的UI。 默认显示年月&#xff1a; 当点击一下加号时切换为年&#xff1a; 当点击减号时切换为日&#xff1a; 即加号、减号点击就是在年月日显示进行切换。给Scrollvie…

动态ip如何自动更换ip

在探讨如何自动更换动态IP地址时&#xff0c;我们首先需要理解动态IP的基本概念。IP地址&#xff0c;即互联网协议地址&#xff0c;分配给每台连接到互联网的设备的唯一标识符。与传统静态IP地址不同&#xff0c;动态IP地址是由网络服务提供商&#xff08;ISP&#xff09;动态分…