当前位置: 首页 > news >正文

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')

  1. 创建一个 包含响应式数据的引用对象
  2. 在js中操作数据时,需要使用 xxx.value 来修改
  3. 在模板中使用数据时,不需要通过 .value来读取,因为 Vue底层会自动解包

ref的参数:可以是基本类型,也可以是引用类型,但是对于这两种数据,响应式处理是完全不同的两套逻辑

  1. 基本类型数据:依然是通过 Object.defineProperty() 中的 get 与 set 进行数据劫持完成响应式
  2. 引用类型数据:Vue3 内部求助了一个新函数 -- reactive 函数,通过ES6自带的 Proxy 方法完成了响应式( 参考下一节 -- reactive 函数实现引用类型响应式 )

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 英文文献翻译方法哪个好?高效率的翻译方法分享
  • Python中导入不同文件夹中的函数
  • 我们的前端开发逆天了!1 小时搞定了新网站,还跟我说 “不要钱”
  • Java 单元测试
  • 大数据-56 Kafka SpringBoot与Kafka 基础简单配置和使用
  • 麒麟V10系统统一认证子系统国际化
  • 大厂linux面试题攻略四之Linux网络服务(二)
  • usb驱动描述符数据结构
  • <数据集>工程机械识别数据集<目标检测>
  • extern关键字在C语言中的作用
  • 【python】三种方式实现将2个3×5数组拼接形成6×5数组
  • 了解消息中间件TongLINK/Q
  • 实验5-1 使用函数计算两点间的距离
  • 以西门子winCC为代表的组态界面,还是有很大提升空间的。
  • 【C++】文件IO流
  • 2019.2.20 c++ 知识梳理
  • CSS进阶篇--用CSS开启硬件加速来提高网站性能
  • FineReport中如何实现自动滚屏效果
  • httpie使用详解
  • java 多线程基础, 我觉得还是有必要看看的
  • JAVA之继承和多态
  • mysql 数据库四种事务隔离级别
  • Node项目之评分系统(二)- 数据库设计
  • Python爬虫--- 1.3 BS4库的解析器
  • SQLServer之创建显式事务
  • vue学习系列(二)vue-cli
  • webgl (原生)基础入门指南【一】
  • Windows Containers 大冒险: 容器网络
  • WinRAR存在严重的安全漏洞影响5亿用户
  • 那些年我们用过的显示性能指标
  • 推荐一款sublime text 3 支持JSX和es201x 代码格式化的插件
  • 2017年360最后一道编程题
  • # SpringBoot 如何让指定的Bean先加载
  • (01)ORB-SLAM2源码无死角解析-(66) BA优化(g2o)→闭环线程:Optimizer::GlobalBundleAdjustemnt→全局优化
  • (1)常见O(n^2)排序算法解析
  • (3)nginx 配置(nginx.conf)
  • (30)数组元素和与数字和的绝对差
  • (C语言)二分查找 超详细
  • (floyd+补集) poj 3275
  • (八)五种元启发算法(DBO、LO、SWO、COA、LSO、KOA、GRO)求解无人机路径规划MATLAB
  • (附源码)apringboot计算机专业大学生就业指南 毕业设计061355
  • (附源码)基于SpringBoot和Vue的厨到家服务平台的设计与实现 毕业设计 063133
  • (力扣)循环队列的实现与详解(C语言)
  • (一)Mocha源码阅读: 项目结构及命令行启动
  • (原創) 如何將struct塞進vector? (C/C++) (STL)
  • (转)nsfocus-绿盟科技笔试题目
  • (转)重识new
  • ***测试-HTTP方法
  • .NET : 在VS2008中计算代码度量值
  • .Net 8.0 新的变化
  • .NET WPF 抖动动画
  • .NET 命令行参数包含应用程序路径吗?
  • .net2005怎么读string形的xml,不是xml文件。
  • .NET8使用VS2022打包Docker镜像
  • .net企业级架构实战之7——Spring.net整合Asp.net mvc