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.mzph.cn/pingmian/24920.shtml

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

相关文章

人工智能与能源约束的矛盾能否化解

以下文章来源&#xff1a;澎湃新闻 人工智能技术在台前展示的是比特世界的算力、算法和数据&#xff0c;但其“轻盈的灵魂”背后则是土地、能源和水等物理世界“沉重的肉身”。根据本文三种情境的模拟测算&#xff0c;未来人工智能发展需要可持续的巨量能源支撑&#xff0c;能源…

基于Python的北京天气数据可视化分析

项目用到库 import numpy as np import pandas as pd import datetime from pyecharts.charts import Line from pyecharts.charts import Boxplot from pyecharts.charts import Pie,Grid from pyecharts import options as opts from pyecharts.charts import Calendar 1.2…

Python应用开发——30天学习Streamlit Python包进行APP的构建(5)

上几次我们已经将一些必备的内容进行了快速的梳理,让我们掌握了streanlit的凯快速上手,接下来我们将其它的一些基础函数再做简单的梳理,以顺便回顾我们未来可能用到的更丰富的函数来实现应用的制作。 st.write_stream 将生成器、迭代器或类似流的序列串流到应用程序中。 …

vue -ant -design 卡片是布局 实现动态计算 当前的 左右间距 实现居中

是这样的一个样式 我们使用display :flex 布局的时候 我们全部剧中 display: flex;align-items: center;justify-content: center; 如果是上述的代码来说的话 总是最后的一个也是会居中的 这样就比较丑 我们好像就没有什么好的办法了 我们这自己写的 肯定没有组件牛 如果有…

三十六篇:未来架构师之道:掌握现代信息系统典型架构

未来架构师之道&#xff1a;掌握现代信息系统典型架构 1. 引言 在企业的数字化转型浪潮中&#xff0c;信息系统架构的角色变得日益重要。它不仅承载了企业的IT战略&#xff0c;更是确保企业在复杂、动态的市场环境中稳定运行的关键。作为信息系统的骨架&#xff0c;一个精心设…

Linux环境在非root用户中搭建(java-tomcat-redis)

注: 本文在内网(离线)环境&#xff0c;堡垒机中搭建&#xff0c;服务器不同可能有所差异&#xff0c;仅供参考 本文安装JDK-20.0.1版本&#xff0c;apache-tomcat-10.1.10版本&#xff0c;redis-6.2.15版本 本文服务器IP假设&#xff1a;192.168.88.133 root用户创建子用户并…

人工智能对话系统源码 手机版+电脑版二合一 全端支持 前后端分离 带完整的安装代码包以及搭建部署教程

系统概述 该系统采用前后端分离的设计模式&#xff0c;前端负责用户界面展示与交互&#xff0c;后端负责数据处理与业务逻辑实现。前后端通过API接口进行通信&#xff0c;实现数据的实时传输与处理。系统支持全端访问&#xff0c;无论是手机还是电脑&#xff0c;都能获得良好的…

u盘内容无故消失了是什么原因?u盘部分内容无故消失了怎么恢复

在数字化时代&#xff0c;U盘作为便携存储设备&#xff0c;承载着许多重要的数据。然而&#xff0c;有时我们可能会遭遇U盘部分内容无故消失的情况&#xff0c;这无疑给我们的工作和生活带来了不小的困扰。本文将为您解析U盘内容消失的可能原因&#xff0c;并分享几招实用的数据…

重生奇迹MU格斗家技能

格斗家有9个技能&#xff0c;其中幽冥青狼拳.斗气爆裂拳.神圣气旋.这3个是武器上带的技能。 回旋踢.幽冥光速拳.炎龙拳.这3个是买羊皮纸学的技能&#xff08;回旋踢是格斗最强技能&#xff09;。 还有3个给自己加BUFF的技能&#xff0c;斗神破&#xff08;增加无视防御的状态&…

移动端 UI 风格,视觉盛宴

移动端 UI 风格&#xff0c;视觉盛宴

linux常用命令及其选项

1、常用命令 1.1、ls 选项说明-a显示所有文件及目录 (包括隐藏文件)-i显示inode-A同 -a选项 &#xff0c;但不列出 "." (目前目录) 及 ".." (父目录)-l列出信息详细(如文件型态、权限、拥有者、文件大小等)-R递归显示(若目录下有文件&#xff0c;则以下之…

htb_office

端口扫描 namp -sSVC 10.10.11.1380&#xff0c;445 80端口 robots.txt 只有/administrator可以访问 Joomla joomscan扫描 joomscan --url http://10.10.11.3/ 版本为4.2.7&#xff0c;存在cve CVE-2023-23752 Joomla未授权访问Rest API漏洞 访问路径 /api/index.php/…

使用OpenCV dnn c++加载YOLOv8生成的onnx文件进行实例分割

在网上下载了60多幅包含西瓜和冬瓜的图像组成melon数据集&#xff0c;使用 EISeg 工具进行标注&#xff0c;然后使用 eiseg2yolov8 脚本将.json文件转换成YOLOv8支持的.txt文件&#xff0c;并自动生成YOLOv8支持的目录结构&#xff0c;包括melon.yaml文件&#xff0c;其内容如下…

Linux宝塔部署数据库连接问题

博主在部署项目时发现网页可以成功部署&#xff0c;但是登录界面一直登录不进去推测是数据库连接问题。 博主当时在IDEA中写的是用户名为root 密码123456 但是在宝塔中因为自己是跟着教程学的所以就顺手把用户名和密码都改了&#xff0c;于是java中的配置和数据库配置连接不上…

利用streamlit结合langchain_aws实现claud3的页面交互

测试使用的代码如下 import streamlit as st from langchain_aws import ChatBedrockdef chat_with_model(prompt, model_id):llm ChatBedrock(credentials_profile_name"default", model_idmodel_id, region_name"us-east-1")res llm.invoke(prompt)re…

mathtype最新注册码下载2024最新分享地址

数学公式编辑器MathType&#xff0c;让在线教育更“数”利 在当今这个数字化、信息化飞速发展的时代&#xff0c;无论是学术研究、教育教学还是日常工作中&#xff0c;都离不开对各种复杂公式的输入与编辑。特别是对于教育工作者和科研工作者而言&#xff0c;如何高效准确地输入…

C++开源项目:pathcopycopyV20源码及运行程序

PathCopyCopy 是一个开源的 Windows 资源管理器扩展项目&#xff0c;旨在为用户提供一个更加高效、便捷的文件路径复制和管理工具。以下是关于 PathCopyCopy 开源项目的详细介绍&#xff1a; 1. 项目概述 2. 项目技术分析 3. 项目功能 4. 项目特点 5. 项目应用场景 6. 项目…

8. C#多线程基础概念

文章目录 一. 目标二. 技能介绍① 进程和线程② 为什么需要多线程③ C#实现多线程的方式④ 线程的操作(创建_终止_挂起_恢复) 一. 目标 进程和线程基本概念为什么需要多线程?C#实现多线程的方式?线程Thread的创建,终止,挂起和恢复? 二. 技能介绍 ① 进程和线程 什么是进程…

计网总结☞物理层

五层协议体系结构->各层的功能有&#xff1a; 物理层 物理层的任务就是尽可能地屏蔽传输媒体的差异&#xff0c;透明地传送比特流&#xff08;注意&#xff1a;传递信息的物理媒体&#xff0c;如双绞线、同轴电缆、光缆等&#xff0c;是在物理层的下面&#xff0c;当做第 0…

Python AI 编程助手:Fitten Code插件

一. 简介 今天为大家推荐一款适配了 Viusal Studio&#xff0c;VS Code(本文使用)&#xff0c;JetBrains 系列(本文使用)以及Vim等多种编译器环境的插件 Fitten Code&#xff0c;Fitten Code 是由非十大模型驱动的 AI 编程助手&#xff0c;它可以自动生成代码&#xff0c;提升…