vue3中的ref与reactive的区别

目录

    • 1、两者的区别
      • 底层实现
      • 响应式引用与响应式对象
    • 2、用法
    • 3、vue3中声明的数组/对象
      • 3.1 通过reactive 声明的Array/Object,给它重新分配一个全新的对象时,会出错、或失去响应式效果
      • 3.2 解决方案
    • 4、cosnt 说明
    • 5、Proxy 与 defineProperty
    • ref 浅层响应式问题的解决方案
    • 总结
      • `ref`
      • `reactive`

1、两者的区别

  • ref:通常用于定义一个响应式引用,例如Number、String、Boolean、Object、Array等
    1. 可以用于定义一个基本数据类型、或者引用数据类型的响应式引用,返回的是一个带有.value属性的对象,但它的.value是一个 Proxy对象,这使得 Vue 也能够正确追踪和响应这些引用的变化
    2. 更简单直观,通过.value来访问和修改值,代码量少,易于理解
    3. 如果是基本类型(单一数据值),ref 也会将它转换成响应式数据,便于监听数据变化
  • reactive:用于定义一个的响应式对象,例如Array、Object等;
    1. 仅用于定义一个引用类型的响应式对象,返回的是深度响应的Proxy对象,对象的任何数据发生变化时(增删改)都会被监测到
    2. 必须是不需要重新分配一个全新的对象的对象
      若重新赋值,会报错或造成响应式丢失,建议使用 ref,详细解释看下方的 vue3中声明数组
  • 无论是ref、reactive最终返回的值都可以监听追踪到值的变化

  • reactive仅用于定义引用数据类型,这是因为底层逻辑用的是 Proxy 实现的,Proxy 不适用 基本数据类型
    尽量不要用 reactive 定义基本类型,有警告错误
    在这里插入图片描述

  • 简单解释一下“重新分配一个全新的对象”,用 ref更合适的原因:
    ref 返回的是响应式引用,在修改值时,用的是.value,而不是直接修改整个ref
    reactive 返回的是响应式对象,在修改值时,是直接赋值(state={}),等同于改掉了整个对象

底层实现

ref会创建一个含有.value属性的对象,Vue 会拦截对 .value 的访问和修改操作,确保在 .value 变化时触发响应式系统的更新。
reactive 主要用于处理复杂对象和数组的响应性。它返回的是一个代理对象,通过 Proxy 拦截对该对象属性的访问和修改,从而实现响应式。

响应式引用与响应式对象

响应式引用

  1. 引用语义:ref返回一个对象,对象中的.value属性是响应式的,我们通过引用.value 来读取和修改值;
  2. 原始值的响应性:读取和修改原始值,也就是读取、修改、重新赋值.value,这并不会改变 ref 对象本身的引用,因此响应性不会丢失;
  3. 在处理值时,ref 会对其进行进行浅层包装,使得可以追踪对象引用的变化,但不会深层追踪对象内部属性的变化。所以通过 ref 声明的 层级太多的对象,可能不会深层追踪对象内部属性的变化,也就是说监测不到所有内部属性的变化。【目前笔者还未测试出来】

响应式对象

  1. reactive 返回一个 proxy 响应式代理对象,只能修改它的属性,不能重新赋值;
  2. 可以追踪其内部所有属性的变化,当属性值被修改时,视图也会自动更新



2、用法

  1. setup() 中使用
    <template><div>{{ count }} </div><div>{{ state.age}} </div><button @click="changeCount">修改count</button><button @click="changeAge">修改Age</button>
    </template>
    <script>import { ref, reactive } from 'vue';export default {setup() {const count = ref(0);const state = reactive({name: 'Alice',age: 30});function changeCount() {count.value++;}function changeAge() {state.age++;}return {count,state,changeCount, // 将方法暴露出去changeAge // 将方法暴露出去};}};
    </script>
    
  2. <script setup> 中使用
    <template><div>{{ count }} </div><div>{{ state.age}} </div><button @click="changeCount">修改count</button><button @click="changeAge">修改Age</button>
    </template>
    <script setup>
    import { ref, reactive } from 'vue';
    const count = ref(0);
    const state = reactive({name: 'Alice',age: 30
    })
    function changeCount() {count.value++;
    }function changeAge() {state.age++;}
    </script>
    



3、vue3中声明的数组/对象

在定义数组时 ,建议使用 ref,这是为了避免 对 reactive 定义的值进行重新分配一个全新的对象时,导致的响应式丢失问题。
当然,如果不是重新分配一个全新的对象,推荐用 reactive,具体讲解请看 3.2 解决方案

案例如下:

<template><div>refList</div><div v-for="(v, i) in refList" :key="i">{{ v }}</div><button @click="changeRef">修改ref</button><div>reactive</div><div v-for="(v, i) in reactiveList" :key="i">{{ v }}</div><button @click="changeReactive">修改reactive</button>
</template><script setup>
import { ref, reactive } from 'vue'
const refList = ref([])
const reactiveList = reactive([])
function changeRef() {// 改变 refList 
}function changeReactive() {// 改变 reactiveList 
}
</script>

3.1 通过reactive 声明的Array/Object,给它重新分配一个全新的对象时,会出错、或失去响应式效果

  1. const 声明的 Array,重新赋值时会报错【提示它是一个只读常量】,这等同于给它重新分配一个全新的对象const 声明的 Object 也一样
const reactiveList = reactive([1, 2, 3])
function changeReactive() {reactiveList = ['01', 1, 2] // 'reactiveList' is constant. eslint[...]
}
//const reactiveList =  reactive({ '0': 1,'1': 2,'2': 3 })
// function changeReactive() {
//   reactiveList = { '0': '01','1': 2,'2': 3 }  // 'reactiveList' is constant. eslint[...]
// }

在这里插入图片描述

  1. let 声明的 Array,重新赋值时可以赋值成功,但它失去了响应式效果,用 let 声明的 Object也一样
let reactiveList = reactive([1, 2, 3])
function changeReactive() {reactiveList = ['01', 2, 3]console.log(reactiveList) // 输出结果是['01', 2, 3],但页面渲染还是[1, 2, 3]
}
// let reactiveList = reactive({ '0': 1,'1': 2,'2': 3 })
// function changeReactive() {
//   reactiveList = { '0': '01','1': 2,'2': 3 }
//   console.log(reactiveList) // 输出结果是{ '0': '01','1': 2,'2': 3 },但页面渲染还是{ '0': 1,'1': 2,'2': 3 }
// }

3.2 解决方案

  • ref 则是创建一个包含原始值的响应式引用(ref)。当 ref 的值改变时,会触发依赖更新。
  • reactive 创建一个深度响应式对象,对 对象的所有嵌套属性进行响应式处理,说简单点,就是只对它进行修改,不重新赋值

方法一:ref 声明 Array,重新分配一个新对象时,不会失去响应,Object也一样

const refList = ref([1, 2, 3])
function changeReactive() {refList.value  = ['01', 2, 3]console.log(refList.value) // 输出结果是['01', 2, 3],页面渲染也是['01', 2, 3]
}const refList = ref({ '0': 1,'1': 2,'2': 3 })
function changeRef() {refList.value = { '0': '01','1': 2,'2': 3 }console.log(refList.value) // 输出结果是{ '0': '01','1': 2,'2': 3 },页面渲染也是{ '0': '01','1': 2,'2': 3 }
}

方法二:用 reactive 声明的Array,修改时使用Array.push、splice等方法,object同理

const reactiveList = reactive([1, 2, 3])
function changeReactive() {reactiveList.push(4) // 输出结果是['01', 2, 3, 4],页面渲染也是[1, 2, 3, 4]console.log(reactiveList)
}



4、cosnt 说明

在 Vue 2 中,使用 const 声明的变量确实是 常量,因为 Vue 2 的响应式系统是基于 Object.defineProperty 实现的,无法追踪 const 变量的重新赋值。
const 声明的变量不可重新赋值,但其引用的对象是可变的,想要改变它,只能改变它内部的属性
let 声明的变量可以重新赋值

但在 Vue 3 中,采用了基于 Proxy 的新响应式系统,const 声明的变量依然可以是响应式的。
在vue3的 setup 函数中,const 声明的变量被称之为 响应式引用响应式对象
所以在vue3中,用const声明一个reactive 对象时,想要改变它,只能改变它内部的属性
如果用 let 声明,虽然可以 对整个对象重新赋值,但这就等同于 给它赋值了一个新的引用,因此之前绑定的响应关系失效了

总结

  • reactive创建响应式对象:修改其属性时是响应式的;
  • const 声明的变量:不可重新赋值,确保引用不变,从而保持响应性;
  • let 声明的变量:可以重新赋值,但重新赋值为新对象,就会失去响应性;
  • const 声明的 reactive 响应式对象,是一个固定引用的响应式对象,使用 const 主要就是为了防止重新赋值,而失去响应性。



5、Proxy 与 defineProperty

reactive方法内部是利用ES6的Proxy API来实现的,这里与Vue2中的defineProperty方法有本质的区别。

  • defineProperty只能单一地监听已有属性的修改或者变化,无法检测到对象属性的新增或删除,而Proxy可以轻松实现;
  • defineProperty无法监听属性值是数组类型的变化,而Proxy可以轻松实现。



ref 浅层响应式问题的解决方案

ref 在处理值时,ref 会对其进行进行浅层包装,使得可以追踪对象引用的变化,但层级太多的对象,可能监测不到所有内部属性的变化。
方法一:结合 toRefs 将 reactive 对象转换为 ref

import { reactive, toRefs } from 'vue';
const state = reactive({nested: {count: 0}
});// 将 reactive 对象的属性转换为 ref
const { nested } = toRefs(state);
// 修改嵌套属性会触发响应式更新
nested.value.count = 1; // 响应式更新

方法二:用this.$forceUpdate()强制刷新

import { ref } from 'vue';
const state = ref({nested: {count: 0}
});// 修改嵌套属性会触发响应式更新
nested.value.count = 1; // 响应式更新
this.$forceUpdate();

方法三:手动修改、触发响应

import { ref } from 'vue';const state = ref({nested: {count: 0}
});function forceUpdate() {state.value = { ...state.value };
}state.value.nested.count = 1; // 这不会触发响应式更新
forceUpdate(); // 这会触发响应式更新



总结

ref

  1. 用于定义一个响应式引用,可以是基本数据类型、引用数据类型,ref会返回一个带有.value属性的对象,而这个.value就是proxy响应式代理对象
  2. 由于.value就是proxy响应式代理对象,所以在读取、修改、重新赋值时针对 .value,这并不会改变 ref对象本身的引用,因此响应性不会丢失;
  3. 在定义 Array 时,更推荐使用 ref,这是因为不能对 reactive 对象重新定义一个全新的引用
  4. 在处理值时,ref 会对其进行进行浅层包装,使得可以追踪对象引用的变化,但层级太多的对象,可能监测不到所有内部属性的变化。可以用(1)this.$forceUpdate()强制刷新;(2)ref结合reactive;(3)手动修改、触发响应

reactive

  1. 仅用于定义一个固定引用的响应式对象,必须是引用数据类型,reactive 返回的是深度响应的proxy对象,对象的所有属性发生变化时,都会被监测到;
  2. 必须是 不需要重新分配一个全新的引用的对象,这是因为修改、重新赋值都是直接对 reactive,如果重新赋值,等同于重新赋值了一个全新的引用,那么之前绑定的响应关系就丢了;






备注:
如有理解错误的观点,请在评论区留言,接受批评和指导

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

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

相关文章

从零开始实现自己的串口调试助手(9)-重置多文本框,保存/载入指令集

重置多文本框 添加多文本控件列表 在构造函数中把我们需要操作的控件归类到对应列表之中 //创建多文本存放数组 - 存放那三列内容 checkBox lineEdi btnfor(int i1;i<9;i){// 添加到按钮数组QString btnName QString("pushButton_t%1").arg(i); //构建对应控件名…

前端工程化:基于Vue.js 3.0的设计与实践

这里写目录标题 《前端工程化&#xff1a;基于Vue.js 3.0的设计与实践》书籍引言本书概述主要内容作者简介为什么选择这本书&#xff1f;结语 《前端工程化&#xff1a;基于Vue.js 3.0的设计与实践》书籍 够买连接—>https://item.jd.com/13952512.html 引言 在前端技术日…

深度解析:短剧市场的发展趋势

一、 短剧视频的兴起 小程序短剧视频是近年来在社交媒体平台上崭露头角的一种内容形式&#xff0c;其独特的表达方式吸引了大量用户的关注&#xff0c;这种类型的视频通常以小幅度、短时长的剧情为主&#xff0c;具有轻松幽默的风格&#xff0c;适合在碎片化的时间作为娱乐消遣…

Shell脚本学习_字符串变量

目录 1.Shell字符串变量&#xff1a;格式介绍 2.Shell字符串变量&#xff1a;拼接 3.Shell字符串变量&#xff1a;字符串截取 4.Shell索引数组变量&#xff1a;定义-获取-拼接-删除 1.Shell字符串变量&#xff1a;格式介绍 1、目标&#xff1a; 能够使用字符串的三种方式 …

HTTP-web服务器

web服务器 web服务器实现了http和相关的tcp连接处理&#xff0c;负责管理web服务器提供的资源&#xff0c;以及对服务器的配置&#xff0c;控制以及拓展等方面的管理 web服务器逻辑实现了http协议&#xff0c;并负责提供web服务器的管理功能&#xff0c;web服务器逻辑和操作系…

makefile与进度条

Linux项目自动化构建工具-make/makefile make是一个命令&#xff0c; makefile是一个文件&#xff0c;保存依赖关系和依赖方法。‘ touch Makefile/makefile mybin:mytest.c//依赖关系 目标文件&#xff1a;依赖文件列表 文件列表的文件之间以空格分隔 gcc -o mybin mytest.…

信号:干扰类别及特征提取(二)

目录 第二部分&#xff1a;特征提取 一&#xff1a;瞬时特征参数 1.零中心归一化瞬时幅度之谱密度的最大值 2.非弱信号段零中心归一化瞬时幅度的标准偏差 3.零中心归一化瞬时幅度绝对值的标准偏差 4.零中心归一化非弱信号段瞬时频率的标准偏差 5.零中心归一化非弱信号段…

【动态规划-BM78 打家劫舍(一)】

题目 描述 你是一个经验丰富的小偷&#xff0c;准备偷沿街的一排房间&#xff0c;每个房间都存有一定的现金&#xff0c;为了防止被发现&#xff0c;你不能偷相邻的两家&#xff0c;即&#xff0c;如果偷了第一家&#xff0c;就不能再偷第二家&#xff1b;如果偷了第二家&…

WPS JS宏获取自动筛选后的行数

//WPS JS宏获取自动筛选后的行数 function getFilterRowCnt(shtRng)//shtRng表示筛选目标工作表范围 {let lngRowCnt 0;for(let rngCell of shtRng.SpecialCells(xlCellTypeVisible).Areas)//获取自动筛选后的单元格行数{lngRowCnt lngRowCnt rngCell.Rows.Count;}return ln…

【ARM Cache 及 MMU 系列文章 6.3 -- ARMv8/v9 Cache Tag数据读取及分析】

请阅读【ARM Cache 及 MMU/MPU 系列文章专栏导读】 及【嵌入式开发学习必备专栏】 文章目录 Cache Tag 数据读取测试代码Cache Tag 数据读取 在处理器中,缓存是一种快速存储资源,用于减少访问主内存时的延迟。缓存通过存储主内存中经常访问的数据来实现这一点。为了有效地管…

【JavaScript】内置对象 - 字符串对象 ⑤ ( 判断对象中是否有某个属性 | 统计字符串中每个字符出现的次数 )

文章目录 一、判断对象中是否有某个属性1、获取对象属性2、判定对象是否有某个属性 二、统计字符串中每个字符出现的次数1、算法分析2、代码示例 String 字符串对象参考文档 : https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/String 一、判…

车载诊断架构 - 引导诊断

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消耗你的人和事,多看一眼都是你的不对。非必要不费力证明自己,无利益不试图说服别人,是精神上的节…

2024年几款优秀的SQL IDE优缺点分析

SQL 工具在数据库管理、查询优化和数据分析中扮演着重要角色。 以下是常见的 SQL 工具及其优缺点。 1. SQLynx 优点&#xff1a; 智能代码补全和建议&#xff1a;采用AI技术提供高级代码补全、智能建议和自动错误检测&#xff0c;大幅提高编写和调试SQL查询的效率。跨平台和…

MFC 教程-回车时窗口退出问题

【问题描述】 MFC窗口默认时&#xff0c;按回车窗口会退出 【原因分析】 默认调用OnOK() 【解决办法】 重写虚函PreTranslateMessage BOOL CTESTMFCDlg::PreTranslateMessage(MSG* pMsg) {// TODO: 在此添加专用代码和/或调用基类// 修改回车键的操作反应 if (pMsg->…

扩散模型条件生成——Classifier Guidance和Classifier-free Guidance原理解析

1、前言 从讲扩散模型到现在。我们很少讲过条件生成&#xff08;Stable DIffusion曾提到过一点&#xff09;&#xff0c;所以本篇内容。我们就来具体讲一下条件生成。这一部分的内容我就不给原论文了&#xff0c;因为那些论文并不只讲了条件生成&#xff0c;还有一些调参什么的…

学习笔记——路由网络基础——静态路由(static)

三、静态路由(static) 1、静态路由 (1)定义 静态路由(Static)&#xff1a;由管理员手动配置和维护的路由。静态路由配置简单&#xff0c;被广泛应用于网络中。此外还可以实现负载均衡和路由备份。 静态路由默认优先级为60&#xff0c;如果想在多条静态路由中让某条路由优选…

Unity基础实践小项目

项目流程&#xff1a; 需求分析 开始界面 选择角色面板 排行榜面板 设置面板 游戏面板 确定退出面板 死亡面板 UML类图 准备工作 1.导入资源 2.创建需要的文件夹 3.创建好面板基类 开始场景 开始界面 1.拼面板 2.写脚本 注意事项&#xff1a;注意先设置NGUI的分辨率大小&…

Scala 练习一 将Mysql表数据导入HBase

Scala 练习一 将Mysql表数据导入HBase 续第一篇&#xff1a;Java代码将Mysql表数据导入HBase表 源码仓库地址&#xff1a;https://gitee.com/leaf-domain/data-to-hbase 一、整体介绍二、依赖三、测试结果四、源码 一、整体介绍 HBase特质 连接HBase, 创建HBase执行对象 初始化…

从0到1:企业办公审批小程序开发笔记

可行性分析 企业办公审批小程序&#xff0c;适合各大公司&#xff0c;企业&#xff0c;机关部门办公审批流程&#xff0c;适用于请假审批&#xff0c;报销审批&#xff0c;外出审批&#xff0c;合同审批&#xff0c;采购审批&#xff0c;入职审批&#xff0c;其他审批等规划化…

使用 stress 命令进行Linux CPU 压力测试

大家好&#xff0c;在现代计算机系统中&#xff0c;对系统性能和稳定性的评估是至关重要的。特别是在服务器环境中&#xff0c;我们需要确保系统能够在高负载情况下稳定运行&#xff0c;以满足用户的需求。而 CPU 是系统中最关键的组件之一&#xff0c;其性能直接影响着整个系统…