Vue 3响应式系统全解析:深入ref、reactive、computed、watch及watchEffect

一、ref与breactive

宏观角度看:

  • 1.ref 用来定义:基本类型数据、对象类型数据
  • 2.reactive 用来定义:对象类型数据

区别:

  • 【ref 】创建的变量必须使用.value
  • 【reactive】 重新分配一个新对象,会失去响应式(可以使用0biect.assign 去整体替换)。

使用原则:

  • 1.若需要一个基本类型的响应式数据,必须使用ref
  • 2.若需要一个响应式对象,层级不深,ref、reactive 都可以。
  • 3.若需要一个响应式对象,且层级较深,推荐使用reactive

二、toRefs与toRef

1.toRef

  • 作用:从 reactive 对象中提取单个属性,创建一个 ref。
  • 使用场景:当你需要对 reactive 对象的某个具体属性进行单独的响应式处理时。

基本用法

import { reactive, toRef } from 'vue';
const person = reactive({ name: 'John Doe' });
const nameRef = toRef(person, 'name');
console.log(nameRef.value); // John Doe

2.toRefs

  • 作用:将 reactive 对象转换为多个 ref 对象的集合。
  • 使用场景:适用于需要将整个 reactive 对象的多个属性分别作为 ref 处理的情况。

基本用法

import { reactive, toRefs } from 'vue';const person = reactive({ name: 'John Doe', age: 30 });
const { name, age } = toRefs(person);
console.log(name.value); // John Doe
console.log(age.value);  // 30

结论

  • toRef 创建单个 ref,用于单个属性的响应式绑定。
  • toRefs 创建多个 ref,用于将整个对象的属性分别响应式绑定。
  • 选择使用 toRef 或 toRefs 取决于你需要对 reactive 对象执行的响应式操作的复杂性。

三、computed计算属性

计算属性是 Vue 3 中一种特殊的响应式变量,它根据其他响应式数据自动计算值。

基本用法

import { computed, ref } from 'vue';
const count = ref(0);
const doubledCount = computed(() => count.value * 2);

特点

  • 自动更新:当依赖的数据变化时,计算属性会自动重新计算。
  • 惰性计算:只有当依赖项变化时,计算逻辑才会执行。
  • 结果缓存:多次访问计算属性会得到相同的结果,而不会重复计算。

四、watch

作用:监视数据的变化(和 Vue2 中的 watch 作用一致)
特点:Vue3 中的 watch 只能监视以下四种数据:

1.ref 定义的数据。
2.reactive 定义的数据。
3.函数返回一个值(getter 函数)。
4.一个包含上述内容的数组。

watch 监听

⭐情况一

监视【ref】 直接写数据名即可,监视的是其 value 值的改变。

示例:

import { watch, ref } from 'vue';const count = ref(0);// 监视 ref 'count' 的变化
watch(count, (newValue, oldValue) => {console.log(`变化前:${oldValue} 变化后: ${newValue}`);
},{deep: true,       	// 深度监听 state 对象内部的变化immediate: true,  	// 当设置为 true 时,watch 会在开始监视后立即执行一次回调函数。flush: 'pre',     	// 控制副作用(如 DOM 更新)的刷新时机。可以是 'pre' 或 'post',或者一个包含这些值的数组。onTrack: () => {},  // 依赖项被追踪时调用onTrigger:() =>{},  // 依赖项变化时调用computed: true    	// 计算属性模式
});

⭐情况二

监视【ref】深层要手动开启深度监视

注意:
若修改的是ref定义的对象中的属性,newValue 和 oldValue 都是新值,因为它们是同一个对象。
若修改整个ref定义的对象,newValue 是新值, oldValue 是旧值,因为不是同一个对象了。

示例:

import { watch, ref } from 'vue';const person = ref({ name: 'John', age: 30 });// 监视 ref 'person' 的深层属性变化
watch(() => person.value, // 使用函数返回 ref 的 value,以确保每次都是最新的值(newValue, oldValue) => {// 回调函数会在 person 对象的属性变化时触发console.log(`变化前:${oldValue.name} 变化后:${newValue.name}`);},{ deep: true } // 开启深度监视
);

在这个例子中:

  • person 是一个通过 ref 创建的响应式引用,它包含一个对象,该对象有 name 和 age 属性。
  • 使用 watch 函数并传入一个函数,该函数返回 person.value。这样做是因为我们需要监视 person 对象内部属性的变化,而不是 person 这个引用本身。
  • 参数 { deep: true } 开启了深度监视,这样 watch 就能够追踪到对象内部属性的变化。
  • 当 person 对象的 name 或 age 属性发生变化时,提供的回调函数会被触发。

注意事项

  • 当你监视 ref 定义的对象并开启深度监视时,newValue 和 oldValue 将代表对象内部发生变化的属性的新旧值。
  • 如果整个 ref 定义的对象被一个新的对象替换,那么 newValue 将是新对象,而 oldValue 将是旧对象。
  • 开启深度监视可能会带来性能上的考虑,因为它会增加 Vue 需要追踪的变化的数量。

⭐情况三

监视【reactive】定义的【对象类型】数据,默认开启深度监视(无法关闭)

示例:

import { reactive, watch } from 'vue';// 使用 reactive 创建一个响应式对象
const person = reactive({name: 'John',details: {age: 30}
});// 监视 reactive 对象的 'details.age' 属性
watch(() => person.details.age,(newValue, oldValue) => {console.log(`Age changed from ${oldValue} to ${newValue}`);}
);

在这个例子中:

  • person 是一个使用 reactive 创建的响应式对象,它包含一个名为 details 的属性,该属性本身也是一个对象,包含 age 属性。
  • 使用 watch 来监视 person.details.age 属性的变化。由于 reactive 默认进行深度监视,因此不需要额外的 deep 选项即可追踪到 details 对象内部的 age 属性变化。

注意事项

  • reactive 创建的响应式对象总是深度监视的,这意味着你无法关闭这个行为。
  • 当你使用 watch 来监视 reactive对象的属性时,即使属性是嵌套的,也不需要 deep 选项,因为深度监视是自动应用的。
  • 如果 reactive 对象的某个属性也是一个对象,并且你想要监视这个嵌套对象的属性,你可以直接使用 watch,并提供一个返回该嵌套属性的函数。

通过这种方式,Vue 3 的 reactive API 提供了一种直观且强大的方式来创建和管理响应式状态,同时 watch API 允许你监视这些状态的变化,无需担心监视的深度问题。

⭐情况四

监视响应式对象中的某个属性,且该属性是基本类型,要写成函数式
监视响应式对象中的某个属性,且该属性是对象类型,可以直接写,也能写函数,更推荐写函数


⭐结论:监视的要是对象里的属性,那么最好写函数式
⭐注意点:若是对象监视的是地址值,需要关注对象内部,需要手动开启深度监视。

基本类型属性的监视

当监视响应式对象中的基本类型属性时,推荐使用函数式的方式来访问该属性的值。这是因为基本类型的属性不会触发对象的重新分配,所以每次获取的都是最新的值。

示例:

import { watch, reactive } from 'vue';const person = reactive({ name: 'John' });// 使用函数式监视响应式对象中的 'name' 属性
watch(() => person.name,(newValue, oldValue) => {console.log(`变化前:${oldValue} 变化后:${newValue}`);}
);

在这个例子中,我们通过函数 () => person.name 来获取 person 对象的 name 属性的值。每次 watch 触发时,都会调用这个函数以确保获取到最新的 name 值。

对象类型属性的监视

当监视响应式对象中的对象类型属性时,可以直接写属性名,也可以使用函数式。推荐使用函数式,尤其是当你需要监视对象内部属性的变化时。

示例:

const person = reactive({ detail: { address: '123 Main St' } });// 使用函数式监视 'detail' 对象内部的 'address' 属性
watch(() => person.detail.address,(newValue, oldValue) => {console.log(`变化前: ${oldValue} 变化后:${newValue}`);},deep: true } // 开启深度监视,以追踪对象内部属性的变化
);

在这个例子中,我们监视的是 person.detail.address。由于 address 是 detail 对象的属性,我们需要开启深度监视 { deep: true } 来确保当 address 发生变化时,watch 能够触发。
我们通过函数 () => person.name 来获取 person 对象的 name 属性的值。每次 watch 触发时,都会调用这个函数以确保获取到最新的 name 值。

⭐情况五:监视上述的多个数据

示例

import { watch, reactive, ref } from 'vue';// 使用 reactive 创建一个响应式对象
const person = reactive({name: 'John',car: 'Toyota'
});// 使用 ref 创建一个响应式引用
const age = ref(30);// 监视 person 对象的 'name' 属性和 age 引用
watch([() => person.name, age], (newValues, oldValues) => {// newValues 和 oldValues 都是数组,分别包含新旧值console.log(`Name changed to ${newValues[0]}, Age changed to ${newValues[1]}`);// 如果只想监视 person.car 的变化,可以这样写:// watch([() => person.car, age], callback, options)
}, { deep: true });

在这个例子中:

  • 使用 reactive 创建了一个响应式对象 person,它包含 name 和 car 属性。
  • 使用 ref 创建了一个响应式引用age。 使用 watch 来监视 person.name 和 age。
  • 这里通过传递一个数组给 watch的第一个参数来实现同时监视多个数据源。数组中的每个元素都是一个返回要监视数据的函数或响应式引用。
  • newValues 和oldValues 参数都是数组,它们包含了所有监视数据源的新旧值。数组中每个元素的顺序与 watch 第一个参数中的顺序相对应。
  • { deep: true } 表示对 person 对象进行深度监视,这样可以确保当对象内部的属性变化时,也能触发 watch
    的回调函数。

注意事项

  • 当监视多个响应式数据源时,确保 newValues 和 oldValues 的索引与 watch 第一个参数中数据源的顺序相匹配。
  • 使用deep: true 选项可以确保当监视的对象或数组内部发生变化时,也能触发回调函数。

五、watchEffect

watchEffect 是 Vue 3 的一个工具,它用来观察你的数据。当你的数据变化时,它会做一些事情。

怎么用?
选择数据:首先,你需要有一些数据,比如一个数字。
告诉 watchEffect 观察这个数据。
决定数据变化时要做的事情:当这个数据变化时,watchEffect 会帮你做一个操作,比如打印信息。
例子
// 假设我们有一个数字,我们想观察它的变化
const count = 0;

// 使用 watchEffect 观察 count 的变化
watchEffect(() => {
// 当 count 变化时,打印 count 的新值
console.log(New value of count is: ${count});
});

// 如果你改变了 count 的值,上面的打印就会执行
停止观察
如果你后面不想观察了,可以告诉 watchEffect 停止。

const stop = watchEffect(() => {
// 逻辑
});

// 想要停止观察时,调用这个函数
stop();

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

EasyMR6.2 全面解读:四大功能深度优化,解锁全新大数据处理和计算体验

在刚刚过去的2024春季发布会上,袋鼠云带来了数栈产品V6.2版本的全新发布。其中,EasyMR 作为数栈V6.2中的一项关键能力,代表了袋鼠云对大数据生态的深入理解和持续创新。 EasyMR(后文统称EMR)是袋鼠云基于 Hadoop、Hiv…

游戏系统设计目录

《游戏系统设计0》跟我一起做游戏服务器《游戏系统设计一》游戏任务系统太复杂,带你一步一步实现 《游戏系统设计二》操蛋,游戏版本要回滚,还好我机智备份了数据库,代码直接拿走《游戏系统设计三》游戏服务器线上出bug,怎么办?急,在线等!热更新

CH341A/B USB转USART/I2C/SPI介绍

CH341A/B USB转USART/I2C/SPI介绍 📍CH341官方文档:https://www.wch.cn/downloads/CH341DS2_PDF.html CH341A/B是一个USB总线的转接芯片,通过USB总线提供异步串口、打印口、并口以及常用的2线和4线等同步串行接口。 🍁芯片封装&a…

前端表单input的简单使用

1.代码结构介绍 2.实战效果

统计学-F检验的用途有哪些?

F检验是一种常用的统计检验方法,主要用于以下几个方面: 方差比较:F检验可以用来比较两个或多个样本或组之间的方差是否存在显著差异。例如,在质量控制中,可以使用F检验来确定不同生产批次的方差是否相同,从…

The layered MVP architecture in Acise

Acise是一款CAx软件开发平台,本文给出Acise中的MVP架构模式的实现思路。 注1:文章内容会不定期更新。 MVP Data Model View Model 参考文献 Erich Gamma. Design Patterns:elements of reusable object-oriented software. Addison Wesley, 1994.Josep…

ETL 和 ELT区别-2

T-ETL和ELT区别_tetl-CSDN博客 ETL(提取、转换和加载)和 ELT(提取、加载和转换)是企业用来从多个源提取数据并将其组合到单个数据库或数据仓库中进行分析的过程。这两种方法都使用以下步骤(但顺序不同)来准…

查询服务器上所有SQL SERVER数据库中是否包含某个字段,且该字段是否包含某个值

公司有一堆相同类别的客户,每个客户都部署了相同的一套系统,每套系统对应一个相同结构的数据库,昨天老板让查一下手机号码177xxxxx248是属于哪个客户的客户。 我要查的这个号码来自于oa_member表中的phone字段,我需要对所有的数据…

6G技术:空天地一体化网络

目录 传输速度: 延迟:0.1毫秒​编辑 应用:远程医疗,自动驾驶; 5G和6G区别:

实测52.4MB/s!全志T3+FPGA的CSI通信案例分享!

CSI总线介绍与优势 CSI(CMOS sensor parallel interfaces)总线是一种用于连接图像传感器和处理器的并行通信接口,应用于工业自动化、能源电力、智慧医疗等领域,CSI总线接口示意图如下所示(以全志科技T3处理器的CSI0为…

2024年51cto视频如何提取

2024年,对于如何提取51cto网站上的视频,许多人都选择在该平台购买自己所需的学习视频。然而,在51cto网页上观看视频将消耗用户的流量。为了解决这一问题,我开发了名为小白51cto工具的软件,使用户能够轻松将视频下载到本…

如何增强Java GCExcel API 的导入和导出性能

前言 GrapeCity Documents for Excel (以下简称GcExcel) 是葡萄城公司的一款服务端表格组件,它提供了一组全面的 API 以编程方式生成 Excel (XLSX) 电子表格文档的功能,支持为多个平台创建、操作、转换和共享与 Microsoft Excel 兼容的电子表格&#xf…

web前端(简洁版)

0. 开发环境 && 安装插件 这里我使用的是vscode开发环境 Auto Rename Tag是语法自动补齐view-in-browser是快速在浏览器中打开live server实时网页刷新 1. HTML 文件基本结构 <html><head><title>第一个页面</title></head><body&g…

开源事件通知库libevent及网络连接管理模块bufferevent详解

目录 1、libevent介绍 1.1、什么是libevent&#xff1f; 1.2、libevent特点 1.3、网络连接管理模块bufferevent 2、bufferevent有什么用&#xff1f; 3、bufferevent的整体设计与实现细节 3.1、整体概况 3.2、evbuffer与bufferevent 3.3、defer callback 4、bufferev…

python爬虫之爬取微博评论(4)

一、获取单页评论 随机选取一个微博&#xff0c;例如下面这个 【#出操死亡女生家属... - 冷暖视频的微博 - 微博 (weibo.com) 1、fnf12&#xff0c;然后点击网络&#xff0c;搜索评论内容&#xff0c;然后预览&#xff0c;就可以查看到网页内容里面还有评论内容 2、编写代码…

前端:防止重复请求的方案

前端&#xff1a;防止重复请求的方案 方案一、axios请求拦截器方案二、把相同的请求拦截掉方案三、&#xff08;最佳推荐&#xff09; 方案一、axios请求拦截器 通过使用 axios拦截器&#xff0c;在请求拦截器中开启全屏Loading&#xff0c;然后再响应拦截器中关闭。 import …

@JsonValue的用法

jackson学习-JSON相关注解 在实际开发过程中对于对象转json有很多的工具类&#xff0c;这里使用的是jackson springboot 中jackson的用法 1 springboot工程本身就集成了jackson 只要是引入 org.springframework.boot spring-boot-starter-web 这个pom的可以直接使用 springbo…

【C#】rdlc报表答应报错:未能加载文件或程序集“Microsoft.SqlServer.Types

文章目录 一、报错信息二、解决方式 一、报错信息 Microsoft.Reporting.WinForms.LocalProcessingException: An error occurred during local report processing. —> Microsoft.Reporting.DefinitionInvalidException: The definition of the report ‘’ is invalid. —&…

Vmware 虚拟机自定义IP地址 - UbuntuServer2204

Vmware 虚拟机自定义IP地址 - UbuntuServer2204 设置网段 选择喜欢的网段&#xff0c; 例如&#xff1a; 166 自定义 IP地址 打开虚拟机&#xff0c; 输入命令查看网卡名 ip addr查看网卡配置文件 ls -al /etc/netplan/编辑网卡配置文件 sudo vim /etc/netplan/00-installe…

C++ //练习 12.20 编写程序,逐行读入一个输入文件,将内容存入一个StrBlob中,用一个StrBlobPtr打印出StrBlob中的每个元素。

C Primer&#xff08;第5版&#xff09; 练习 12.20 练习 12.20 编写程序&#xff0c;逐行读入一个输入文件&#xff0c;将内容存入一个StrBlob中&#xff0c;用一个StrBlobPtr打印出StrBlob中的每个元素。 环境&#xff1a;Linux Ubuntu&#xff08;云服务器&#xff09; 工…