ref函数
Vue2 中的ref
首先我们回顾一下 Vue2 中的 ref。
ref 被用来给元素或子组件注册引用信息。引用信息将会注册在父组件的 $refs 对象上。如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例:
<!-- `vm.$refs.p` will be the DOM node -->
<p ref="p">hello</p><!-- `vm.$refs.child` will be the child component instance -->
<child-component ref="child"></child-component>
其实也就是给元素或者是子组件打上标记,然后通过在父组件中 通过 this.refs.xxx拿到这个 DOM元素或者是组件实例,进而操作 DOM 或者访问组件实例。
在官方文档上声明,ref是一个特殊的属性,$refs是一个对象,持有注册过 ref attribute 的所有 DOM 元素和组件实例。
Vue3 setup 中直接定义的数据为什么改变之后,视图不同步
如果我按照上一节 setup 中的模式 ,直接创建变量且赋基础值,然后试图改变变量的值,我们看看会发生什么
<template><p>{{ name }}</p><button @click="changeName">改变名称</button>
</template><script>
export default {name: "App",setup() {let name = "al";function changeName() {name = '汤圆仔'}return {changeName,name,};},
};
</script>
但是,在我点击按钮之后,我发现页面上的名称并没有发生改变。造成的原因可能有两个,
第一个可能是,数据改变了,但是视图没更新。第二种则是,数据本身就没改变所以不更新视图。
验证:改变数据之后,直接打印修改后的变量

从这里可以看到,我们修改的数据其实时发生了改变的,但是页面上却没有更新,这在 Vue2 中是不会存在的,因为 Vue2 中定义在 data 中的数据时响应式的,所以我们可以得出下面这个结论:那就是 直接在 setup 中声明的变量,不是响应式的。
为了解决这一个问题,Vue3 推出了 ref 函数 用来将 setup 中定义的变量转化为响应式。
Vue3 中的 ref 函数
首先,Vue3 中的 ref 是一个函数,区别于Vue2 中的ref是一个特殊属性。作用是将数据转化为响应式。在使用时,需要引入,然后将要转化的数据传入到 ref() 函数中。
<template><p>{{ name }}</p><button @click="changeName">改变名称</button>
</template><script>
import { ref } from 'vue'
export default {name: "App",setup() {let name = ref("al");function changeName() {name = '汤圆仔'console.log(name,'name');}return {changeName,name,};},
};
</script>
到了这一步,我们再次点击按钮 ,发现还是同样的结果,数据改变了,但是视图没刷新。不是说好的 通过 ref() 函数就能将数据转化为响应式的么,我这都转化了,为啥还是这样?
ref() 函数处理基本数据类型
对于上面的 基本数据类型 name,我们可以在改变值之前,先看看这个 name 变量,被 ref() 函数处理成了什么样子。

可以看到 ref()函数将基本类型数据转化为了一个 ref引用对象(RefImpl对象),同时我们展开对象查看内部属性可以发现存在以下属性。

dep对象暂时猜测和Vue2一致,用来收集分发依赖的。四个带有_前缀的属性一般也是用来给 Vue底层源码使用的,所以这个value一看就是给我们开发者的。
而且我们还可以发现,鼠标放到value属性值的省略号上之后,提示是通过gettrt 方法获得的值,这和Vue2获取响应式数据一致。
然后打开 Prototype 属性,可以看到,针对于这个基础类型的值的 getter 和 setter方法,以及真正的value值。将方法以及初始值放在原型上是利用原型的作用,避免外层数据繁杂。然后将真正的value暴露给外层是为了方便开发者使用。

到这里我们就可以大胆的推论得出:ref() 函数在处理基础类型的值时,通过将其转化为了 RefImpl 引用实例对象后,还是通过 getter 和 setter 来实现响应式的。
得到结论之后还不够,我们要知道怎么去修改或访问通过ref()函数转化的基础数据啊。
在上面通过 name = '汤圆仔' 直接修改属性值被证明是已经行不通了的。因为这样修改之后,相当于是把这个响应式属性直接变成了一个基础类型的值,从而失去了响应式功能。
而看着 RefImpl 引用实例对象中的属性,我们能理解并使用的也就只有 value 属性了。所以当我们修改数据的时候,通过 name.value = '汤圆仔',就能在保证响应式的前提下修改数据了。
function changeName() {name.value = '汤圆仔'console.log(name,'name');
}

同理,在模板中使用数据的时候,我们好像也应该通过 插值语法的形式 {{ name.value }} 去使用,但是在你真这么做之后,你会发现,页面渲染其实错误了。
<p>姓名:{{ name.value }}</p>

这其实是因为,Vue3底层设计考虑到了这一问题,在模板中使用变量,Vue3判断当前为插值语法,且使用的是通过 ref() 函数进行转化过后的响应式数据后,会自动解包,自动读取value值,而不需要开发者手动 xxx.value 去获取属性值。所以,我们还是像以前一样,通过插值语法直接使用该属性即可。

ref() 函数处理引用类型值
上面说的是ref()函数对于基本数据类型的值的处理。但是如果我的数据比较多,那我分别调用ref比较麻烦,所以 ref() 函数也支持传入对象形式的数据
<template><p>姓名:{{ userInfo.name }}</p><p>姓名:{{ userInfo.age }}</p><p>姓名:{{ userInfo.work }}</p><button @click="changeName">改变名称</button>
</template><script>
import { ref } from 'vue'
export default {name: "App",setup() {let userInfo = ref({name: 'al',age: 29,work:'前端'});function changeName() {console.log(userInfo,'userInfo');}return {changeName,userInfo,};},
};
</script>

点击按钮,控制台打印出当前通过ref()函数转化后的 userInfo 属性,我们能发现返回的还是一个RefImpl引用实例对象,而且 value还是通过 getter转化为响应式的。此时我们不点开 value的值,按照基本类型的处理方式推测一下,此时的value应该是一个对象。

于此同时我们也应该想到一个问题,那就是在Vue2 中,实现了对象的深层响应,那么在Vue3中不可能丢掉这个功能,所以我们可以推断此时 ref()函数对于引用类型的值也做了深层响应式,也应该是针对于引用类型中每个属性都应该返回一个refImpl引用对象实例,以此来保障数据完全深层响应。
那么我们可以推断出,当我们在改变unseInfo内部 name 属性的时候,我们也应该通过 name.value 去修改,也就是说当我们需要修改对象内部属性时,我们需要这样做:先通过 userInfo.value 拿到转化为响应式的 userInfo 对象,然后修改name时,也需要拿到 name 的value去修改
function changeName() {console.log(userInfo);userInfo.value.name.value = '汤圆仔'
}
但是这时候我们发现页面报错了,且提示信息为:不能在一个字符串 al 中读取 value 属性。也就是说 userInfo.value.name 之后是取不到 value属性的。这么搞就有点混乱了啊,那我到底是加还是不加呢?
为了解决这个问题,我们看看 userInfo.value 到底返回的是个啥玩意。点开 value属性之后我们发现 value 属性并不是一个 refImpl引用实例对象,而是一个 Proxy 代理对象。而且这个代理对象上的每个属性只有键值对对应,并没有所谓的 value 属性,所以这个时候我们就需要明白一个问题:Vue3 对于基础类型和引用类型转化为响应式,用的是不同的底层逻辑。
针对于基本类型的数据,Vue3走的还是和Vue2一样的 defineProperty 的getter、setter的数据劫持的方式实现的响应式。
而针对于引用类型的数据,Vue3 走的则是通过Proxy代理的方式实现的响应式( 下一节仔细讲讲怎么通过Proxy实现引用类型的响应式转化 )

搞明白了上面这个value属性值的问题,当我们需要改变引用类型中的数据时,我们就可以这样做
function changeName() {userInfo.value.name = '汤圆仔'
}
总结
ref的作用:定义一个响应式的数据
ref的语法:let xxx = ref('initvalue')
- 创建一个 包含响应式数据的引用对象
- 在js中操作数据时,需要使用 xxx.value 来修改
- 在模板中使用数据时,不需要通过 .value来读取,因为 Vue底层会自动解包
ref的参数:可以是基本类型,也可以是引用类型,但是对于这两种数据,响应式处理是完全不同的两套逻辑
- 基本类型数据:依然是通过 Object.defineProperty() 中的 get 与 set 进行数据劫持完成响应式
- 引用类型数据:Vue3 内部求助了一个新函数 -- reactive 函数,通过ES6自带的 Proxy 方法完成了响应式( 参考下一节 -- reactive 函数实现引用类型响应式 )
相关文章:
ref函数
Vue2 中的ref 首先我们回顾一下 Vue2 中的 ref。 ref 被用来给元素或子组件注册引用信息。引用信息将会注册在父组件的 $refs 对象上。如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例࿱…...
7/30 bom和dom
文档对象mox 浏览器对象模型...
【Golang 面试 - 进阶题】每日 3 题(五)
✍个人博客:Pandaconda-CSDN博客 📣专栏地址:http://t.csdnimg.cn/UWz06 📚专栏简介:在这个专栏中,我将会分享 Golang 面试中常见的面试题给大家~ ❤️如果有收获的话,欢迎点赞👍收藏…...
MySQL,GROUP BY子句的作用是什么?having和where的区别在哪里说一下jdbc的流程
GROUP BY 子句的作用是什么 GROUP BY 字段名 将数据按字段值相同的划为一组,经常配合聚合函数一起使用。 having和where的区别在哪里 where是第一次检索数据时候添加过滤条件,确定结果集。而having是在分组之后添加结果集,用于分组之后的过…...
1._专题1_双指针_C++
双指针 常见的双指针有两种形式,一种是对撞指针,一种是左右指针。对撞指针:一般用于顺序结构中,也称左右指针。 对撞指针从两端向中间移动。一个指针从最左端开始,另一个从最右端开始,然后逐渐往中间逼近…...
Spring集成ES
RestAPI ES官方提供的java语言客户端用以组装DSL语句,再通过http请求发送给ES RestClient初始化 引入依赖 <dependency><groupId>org.elasticsearch.client</groupId><artifactId>elasticsearch-rest-high-level-client</artifactId> </d…...
力扣高频SQL 50题(基础版)第二十六题
文章目录 力扣高频SQL 50题(基础版)第二十六题1667.修复表中的名字题目说明实现过程准备数据实现方式结果截图总结 力扣高频SQL 50题(基础版)第二十六题 1667.修复表中的名字 题目说明 表: Users ----------------…...
WIFI 接收机和发射机同步问题+CFO/SFO频率偏移问题
Synchronization Between Sender and Receiver & CFO Correction 解决同步问题和频率偏移问题是下面论文的关键,接下来结合论文进行详细解读 解读论文:Verification and Redesign of OFDM Backscatter 论文pdf:https://www.usenix.org/s…...
ubuntu安装并配置flameshot截图软件
参考:flameshot key-bindins 安装 sudo apt install flameshot自定义快捷键 Settings->Keyboard->View and Customize Shortcuts->Custom Shortcuts,输入该快捷键名称(自定义),然后输入command(…...
【Linux】CentOS更换国内阿里云yum源(超详细)
目录 1. 前言2. 打开终端3. 确保虚拟机已经联网4. 备份现有yum配置文件5. 下载阿里云yum源6. 清理缓存7. 重新生成缓存8. 测试安装gcc 1. 前言 有些同学在安装完CentOS操作系统后,在系统内安装比如:gcc等软件的时候出现这种情况:(…...
Leetcode49. 字母异位词分组(java实现)
今天我来给大家分享的是leetcode49的解题思路,题目描述如下 如果没有做过leetcode242题目的同学,可以先把它做了,会更好理解异位词的概念。 本道题的大题思路是: 首先遍历strs,然后统计每一个数组元素出现的次数&#…...
OpenJudge | 字符串中最长的连续出现的字符
总时间限制: 1000ms 内存限制: 65536kB 描述 求一个字符串中最长的连续出现的字符,输出该字符及其出现次数,字符串中无空白字符(空格、回车和tab),如果这样的字符不止一个,则输出第一个 输入 首先输入N…...
11day-C++list容器使用
这里写目录标题 1. list的介绍及使用1.1 list的介绍1.2.1 list的构造1.2.2 list iterator的使用1.2.3 list capacity1.2.4 list element access1.2.5 list modifiers1.2.6 list的迭代器失效 2. list的模拟实现2.1 list的反向迭代器 1. list的介绍及使用 1.1 list的介绍 list的…...
docker 常用管理命令及数据备份
docker 常用管理命令及数据备份 常用管理命令 重启 cd share docker compose restart 停止 cd share docker compose stop 启动 cd share ./deploy.sh 升级 cd share ./deploy.sh 查看日志 cd share docker compose logs -f 数据备份 以下备份相关命令均要求在doc…...
前端开发:Vue2.0桌面组件库-Element
引入Element的步骤: 1.在vscode终端中执行命令(需要联网) 下载成功 2.在main.js中导入element.ui组件库。 同上,自定义的组件需要先在根组件中引入。 3.访问官网,复制调整代码...
Java常见的面试二
1、普通类和抽象类有那些区别 普通类中不能有抽象方法,抽象类中可以有抽象方法普通类可以直接实例化,抽象类不能直接实例化 2、抽象类能够使用final修饰吗 不能,抽象类是由子类继承的,但是final修饰的类不能被继承。两者矛盾所以…...
【Qt】QLCDNumberQProgressBarQCalendarWidget
目录 QLCDNumber 倒计时小程序 相关属性 QProgressBar 进度条小程序 相关设置 QLCDNumber QLCDNumber是Qt框架中用于显示数字或计数值的小部件。通常用于显示整数值,例如时钟、计时器、计数器等 常用属性 属性说明intValueQLCDNumber显示的初始值(int类型)va…...
C++ 代码实现局域网即时通信功能 (windows 系统 客户端)
本项目使用C实现具备多个客户端和服务器端即时通信聊天功能软件 一:项目内容 使用C实现一个具备多客户端和一个服务器端即时通信功能的聊天软件。 本项目的目的是 学习在windows平台下,进行C网络开发的基本概念:TCP/IP socket通信࿰…...
机器人阻抗控制实现方法及其存在的科学问题
一、机器人阻抗控制的实现方法 机器人阻抗控制主要分为两种方法:基于位置的阻抗控制和基于力的阻抗控制。 基于位置的阻抗控制: 工作原理:让机器人电机在位置模式下工作,通过发送目标位置和速度实现阻抗特性。主要目的:控制机器人的位置精度和运动轨迹。特点:该方法侧重…...
解决:xxx.xxx/res/modules/.ds_store: error: the file name must end with .xml 问题
解决:xxx.xxx/res/modules/.ds_store: error: the file name must end with .xml 问题 该问题是由于Android Studio校验到布局文件中存在不以.xml后缀名结尾的文件,这个文件就是.DS_store,它是Mac上系统自动创造的隐藏文件,把该文…...
内存分配函数malloc kmalloc vmalloc
内存分配函数malloc kmalloc vmalloc malloc实现步骤: 1)请求大小调整:首先,malloc 需要调整用户请求的大小,以适应内部数据结构(例如,可能需要存储额外的元数据)。通常,这包括对齐调整,确保分配的内存地址满足特定硬件要求(如对齐到8字节或16字节边界)。 2)空闲…...
聊聊 Pulsar:Producer 源码解析
一、前言 Apache Pulsar 是一个企业级的开源分布式消息传递平台,以其高性能、可扩展性和存储计算分离架构在消息队列和流处理领域独树一帜。在 Pulsar 的核心架构中,Producer(生产者) 是连接客户端应用与消息队列的第一步。生产者…...
《用户共鸣指数(E)驱动品牌大模型种草:如何抢占大模型搜索结果情感高地》
在注意力分散、内容高度同质化的时代,情感连接已成为品牌破圈的关键通道。我们在服务大量品牌客户的过程中发现,消费者对内容的“有感”程度,正日益成为影响品牌传播效率与转化率的核心变量。在生成式AI驱动的内容生成与推荐环境中࿰…...
最新SpringBoot+SpringCloud+Nacos微服务框架分享
文章目录 前言一、服务规划二、架构核心1.cloud的pom2.gateway的异常handler3.gateway的filter4、admin的pom5、admin的登录核心 三、code-helper分享总结 前言 最近有个活蛮赶的,根据Excel列的需求预估的工时直接打骨折,不要问我为什么,主要…...
【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)
可以使用Sqliteviz这个网站免费编写sql语句,它能够让用户直接在浏览器内练习SQL的语法,不需要安装任何软件。 链接如下: sqliteviz 注意: 在转写SQL语法时,关键字之间有一个特定的顺序,这个顺序会影响到…...
OkHttp 中实现断点续传 demo
在 OkHttp 中实现断点续传主要通过以下步骤完成,核心是利用 HTTP 协议的 Range 请求头指定下载范围: 实现原理 Range 请求头:向服务器请求文件的特定字节范围(如 Range: bytes1024-) 本地文件记录:保存已…...
【Web 进阶篇】优雅的接口设计:统一响应、全局异常处理与参数校验
系列回顾: 在上一篇中,我们成功地为应用集成了数据库,并使用 Spring Data JPA 实现了基本的 CRUD API。我们的应用现在能“记忆”数据了!但是,如果你仔细审视那些 API,会发现它们还很“粗糙”:有…...
土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等
🔍 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术,可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势,还能有效评价重大生态工程…...
AI编程--插件对比分析:CodeRider、GitHub Copilot及其他
AI编程插件对比分析:CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展,AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者,分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...
2025季度云服务器排行榜
在全球云服务器市场,各厂商的排名和地位并非一成不变,而是由其独特的优势、战略布局和市场适应性共同决定的。以下是根据2025年市场趋势,对主要云服务器厂商在排行榜中占据重要位置的原因和优势进行深度分析: 一、全球“三巨头”…...
