vue3 ref的使用、问题及源码分析;引用型变量和原始类型变量的复制值

news2025/4/2 7:18:17

文章目录

  • ref定义及作用
    • 用法
    • 源码
  • 实验一 修改原变量和ref后的值
    • 原始数据类型
    • 对象类型
    • 总结
  • 实验二 props的ref

ref定义及作用

可以将 ref 看成 reactive 的一个变形版本,这是由于 reactive 内部采用 Proxy 来实现,而 Proxy 只接受对象作为入参,这才有了 ref 来解决值类型的数据响应(原始数据类型共有7个,分别是:String/ Number /BigInt /Boolean /Symbol /Null /Undefined。),如果传入 ref 的是一个对象,内部也会调用 reactive 方法进行深层响应转换,即ref允许我们创建使用任何值类型的响应式。

用法

ref() 将传入的参数包装为一个带有 value 属性的 ref 对象,对于传入的对象也会用value进行一层包装。

源码

export function ref(value?: unknown) {
  return createRef(value)
}

/**
 * @description: 
 * @param {rawValue} 原始值 
 * @param {shallow} 是否是浅观察 
 */
function createRef(rawValue: unknown, shallow = false) {
  // 如果已经是ref直接返回
  if (isRef(rawValue)) {
    return rawValue
  }

  // 如果是浅观察直接观察,不是则将 rawValue 转换成 reactive ,
  // reactive 的定义在下方 
  let value = shallow ? rawValue : convert(rawValue)

  // ref 的结构
  const r = {
    // ref 标识
    __v_isRef: true,
    get value() {
      // 依赖收集
      track(r, TrackOpTypes.GET, 'value')
      return value
    },
    set value(newVal) {
      if (hasChanged(toRaw(newVal), rawValue)) {
        rawValue = newVal
        value = shallow ? newVal : convert(newVal)
        // 触发依赖
        trigger(
          r,
          TriggerOpTypes.SET,
          'value',
          __DEV__ ? { newValue: newVal } : void 0
        )
      }
    }
  }
  return r
}

// 如是是对象则调用 reactive, 否则直接返回 
const convert = <T extends unknown>(val: T): T =>
  isObject(val) ? reactive(val) : val

实验一 修改原变量和ref后的值

原始数据类型

我们定义一个字符串,使用ref构造响应式对象valueRef,然后修改valueRef的值

let value3 = "123";
let valueRef = ref(value3);
valueRef.value = "456";
console.log(valueRef.value);
console.log(value3);

此时输出是怎样的结果呢?
在这里插入图片描述

会发现valueRef.value发生改变,但是原来的变量value3 并没有改变

那么修改value3的值呢

let value3 = "123";
let valueRef = ref(value3);
// valueRef.value = "456";
value3 = "456";
console.log(valueRef.value);
console.log(value3);

在这里插入图片描述
会发现value3发生了改变,但是valueRef.value并未改变,这是符合刚开始的预期的,但是如果换成对象类型呢

对象类型

定义如下:

let value4 = { value: "123" };
let valueRef2 = ref(value4);
valueRef2.value.value = "456";
console.log(valueRef2.value);
console.log(value4);

修改valueRef2.value.value的值,此时结果如图:
在这里插入图片描述
会发现原来的value4 也会发送改变

修改value4的值

let value4 = { value: "123" };
let valueRef2 = ref(value4);
// valueRef2.value.value = "456";
value4.value = "456";
console.log(valueRef2.value);
console.log(value4);

结果如下:
在这里插入图片描述
发现valueRef2.value.value的值也会发生改变

总结

实验发现,当对原始数据类型使用ref响应后,响应式的变量和原变量已经完全脱钩,修改任一一方的值都不会引起另外一方的改变;然而如果是对对象使用ref响应,则修改其中一方的值都会引起另外一方的改变

分析原理:还是要回到javascript中变量的存储以及复制值,对于原始数据类型的变量会存于中,对于引用类型的变量会存于中。

原始类型的值复制时,原来变量的值会被复制到新变量的位置,即会产生一个新的副本,两者是完全独立的;但是对于引用类型的复制时,存储在原来变量的值也会复制到新变量,但区别在于,这里复制的值其实是一个指针,共同指向存储在堆中的对象。操作完成后,两个变量实际上指向同一个对象,因此其中一个值进行变化,另外的值同样也会反映出来。而传递给ref函数和createRef函数时相当于值的复制,自然出现了以上两种不同的结果

实验二 props的ref

父组件中定义一个columns数组

<template>
  <div>
    <div>{{ value }}</div>
    <div>{{ value2 }}</div>
    <child v-model:value="value" :value2="value2" :columns="columns"></child>
  </div>
</template>

<script lang="ts" setup>
import { ref } from "vue";
import child from "@/views/test/vmodel-test/child.vue";
import { ColumnProps } from "@/components/ProTable/interface";
let value = ref("111");
let value2 = ref({ value: 222 });
const columns: ColumnProps[] = [
  { type: "index", label: "#", width: 150 },
  { prop: "meta.title", label: "菜单名称", align: "left", search: { el: "input" } },
  { prop: "meta.icon", label: "菜单图标" },
  { prop: "name", label: "菜单 name", search: { el: "input" } },
  { prop: "path", label: "菜单路径", width: 300, search: { el: "input" } },
  { prop: "component", label: "组件路径", width: 300 },
  { prop: "operation", label: "操作", width: 250, fixed: "right" }
];
</script>

在子组件中接收:

<script lang="ts" setup name="child">
import { ref, computed } from "vue";
import { ColumnProps } from "@/components/ProTable/interface";

const props = defineProps<{ value: string; value2: { value: number }; columns: ColumnProps[] }>();

const columnsRef = ref(props.columns);
columnsRef.value[1].isShow = false;
const addAttrFunc = (columns: ColumnProps[]) => {
  columns.forEach(col => {
    col.isShow = col.isShow ?? true;
  });
};
addAttrFunc(columnsRef.value);
console.log(columnsRef.value);
console.log(props.columns);
</script>

使用ref(props.columns)生成一个响应式变量columnsRef ,此时对columnsRef进行修改,直接赋值修改和调用foreach函数进行修改,结果如下
在这里插入图片描述
发现props.columns的值也发生了改变即props的值发生了变化
但是如果是对value使用ref函数获得的响应对象进行修改

const valueRef = ref(props.value);
valueRef.value = "456";
console.log(valueRef.value);
console.log(props.value);

结果如图,props.value是不会发生改变的,原因同与实验一相同,产生一个独立的副本

在这里插入图片描述

对于props值本身的修改,不论是原始类型还是引用型都是会直接报错的

props.value = "456";
props.columns[1].isShow = false;

在这里插入图片描述
但是如果调用foreach方法,则不会报错

props.columns.forEach(col => {
  col.isShow = col.isShow ?? true;
});

但却会对props.columns造成改变
这样的话岂不是违法了props的单向数据流原则?

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

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

相关文章

Mybatis的学习笔记

一、Mybatis的使用&#xff1a; 1.1 Mybatis的介绍&#xff1a; Mybatis是一款优秀的持久层&#xff08;负责将数据保存到数据库额那一层&#xff09;框架&#xff0c;用于简化JDBC的开发。 框架的概念是&#xff1a;框架是一个半成品的软件&#xff0c;是一套可重用的、通用…

Vue2入门学习汇总

1.介绍及安装 1.1 介绍 Vue是一套构建用户界面的渐进式框架。Vue只关注视图层&#xff0c;采用自底向上增量开发的设计。Vue的目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件。 学习vue之前主要掌握的知识&#xff1a;HTML、CSS、JavaScript、TypeScript …

阿里云云解析DNS核心概念与应用

文章目录 1.DNS解析基本概念1.1.DNS基本介绍1.2.域名的分层结构1.3.DNS解析原理1.4.DNS递归查询和迭代查询的区别1.5.DNS常用的解析记录 2.使用DNS云解析将域名与SLB公网IP进行绑定2.1.进入云解析DNS控制台2.2.添加域名解析记录2.3.验证解析是否生效 1.DNS解析基本概念 DNS官方…

RNN模型简单理解和CNN区别

目录 神经网络&#xff1a;水平方向延伸&#xff0c;数据不具有关联性 ​ RNN&#xff1a;在神经网络的基础上加上了时间顺序&#xff0c;语义理解 ​RNN: 训练中采用梯度下降&#xff0c;反向传播 ​ 长短期记忆模型 ​输出关系&#xff1a;1 toN&#xff0c;N to N 单入…

还在手动调节温湿度?一招实现远程监控

无论是在医疗保健、食品加工、物流仓储还是制造业&#xff0c;精确的温湿度控制都是保障产品质量、设备稳定性以及人员健康的重要因素。 无论是在实验室中追求精确数据&#xff0c;还是在农田中寻求最佳生长条件&#xff0c;温湿度监控都在发挥着不可或缺的作用&#xff0c;塑造…

赴日程序员 RUN个地方生活最低要多少钱?

要说最低成本的话&#xff0c;那肯定不是以投资的方式&#xff0c;而是要以工作的方式才行&#xff0c;那么不管是去哪个国家工作&#xff0c;都是需要有一个基本学历在的&#xff0c;像日本的话基本上是需要本科的学历&#xff0c;如果是专科的话&#xff0c;需是要相关专业或…

VS2017编译同事VS2022创建的库,报错无法运行rc.exe

项目场景&#xff1a; 项目场景&#xff1a;有一个qt的版本&#xff0c;迁移到VS中进行开发编译&#xff0c;同事使用的是VS2022已经编译成功。但是拷贝到我电脑&#xff0c;我使用VS2017打开编译&#xff0c;却报错&#xff1a;报错无法运行rc.exe。 原因分析&#xff1a; 1.…

【前端|Javascript第5篇】全网最详细的JS的内置对象文章!

前言 在当今数字时代&#xff0c;前端技术正日益成为塑造用户体验的关键。我们在开发中需要用到很多js的内置对象的一些属性来帮助我们更快速的进行开发。或许你是刚踏入前端领域的小白&#xff0c;或者是希望深入了解内置对象的开发者&#xff0c;不论你的经验如何&#xff0c…

使用@antv/x6-vue-shape 遇到的问题

最近用antv/x6开发一个功能&#xff0c;遇到的坑太多了&#xff0c;心累啊。。。 想用官方提供的antv/x6-vue-shape &#xff0c;目的是使用vue组件创建画布元素。 官方文档&#xff1a; 链接&#xff1a;使用 HTML/React/Vue/Angular 渲染 | X6 使用npm install 安装 报错…

【工具】 删除Chrome安装的“创建快捷方式”

创建Chrome的快捷方式&#xff0c;可以放在桌面&#xff0c;想用时双击就可以打开网页&#xff0c;比书签&#xff08;brookmark&#xff09;结构化管理更方便。 但是&#xff0c;安装一时爽&#xff0c;卸载有问题。 如果用 windows 控制面板\所有控制面板项\程序和功能 卸载…

C++超基础语法

&#x1f493;博主个人主页:不是笨小孩&#x1f440; ⏩专栏分类:数据结构与算法&#x1f440; C&#x1f440; 刷题专栏&#x1f440; C语言&#x1f440; &#x1f69a;代码仓库:笨小孩的代码库&#x1f440; ⏩社区&#xff1a;不是笨小孩&#x1f440; &#x1f339;欢迎大…

企业到什么阶段需要进行数字化转型?

数字化转型并不是一个一刀切的过程&#xff0c;也不存在普遍规定企业必须在何时经历数字化转型的特定阶段。然而&#xff0c;有一些常见的触发因素或情况往往会促使企业考虑或踏上数字化转型之旅&#xff1a; 1.不断变化的商业格局&#xff1a;当企业面临客户行为、市场动态或…

visual studio 2022配置

前提&#xff1a;我linux c 开发 一直在使用vscode 更新了个版本突然代码中的查找所用引用和变量修改名称不能用了&#xff0c;尝试了重新配置clang vc都不行&#xff0c;估计是插件问题&#xff0c;一怒之下改用visual studio 2022 为了同步2个IDE之间的差别&#xff0c;目前…

Unity小项目__打砖块

//1.添加地面 1&#xff09;创建一个平面&#xff0c;命名为Ground。 2)创建一个Materials文件夹&#xff0c;并在其中创建一个Ground材质&#xff0c;左键拖动其赋给平面Plane。 3)根据喜好设置Ground材质和Ground平面的属性。 // 2.创建墙体 1&#xff09;创建一个Cube&…

无涯教程-Perl - unshift函数

描述 此函数按顺序将LIST中的元素放在ARRAY的开头。这与shift()相反。 语法 以下是此函数的简单语法- unshift ARRAY, LIST返回值 此函数返回ARRAY中新元素的数量。 例 以下是显示其基本用法的示例代码- #!/usr/bin/perl -warray ( 1, 2, 3, 4);print "Value of a…

关于svg

1.svg是一种基于XML语法的图像格式&#xff0c;是一种图像格式&#xff0c;是一种对图像的描述&#xff0c;不是基于像素处理的&#xff0c;因此他的本质是文件&#xff0c;而且体积小不易失真。 2.svg文件可以直接插入网页&#xff0c;也就是html文件中&#xff0c;成为dom的…

批量爬虫采集完成任务

批量爬虫采集是现代数据获取的重要手段&#xff0c;然而如何高效完成这项任务却是让许多程序员头疼的问题。本文将分享一些实际操作价值高的方法&#xff0c;帮助你提高批量爬虫采集的效率和专业度。 目标明确&#xff0c;任务合理划分&#xff1a; 在开始批量爬虫采集前&…

带你快速认识Java异常和bug的解决过程

一 常见异常介绍 1.1 编译时异常 1. 操作数据库产生的异常 SQLException 2. 操作文件产生的异常 IOException 3. 文件找不到的异常 FileNotFoundException 4. 类找不到异常 ClassNotFoundException 5. 非法参数异常 IllegalArguementException 1.2 运行时异常 1. 空指针异…

ONNX版本YOLOV5-DeepSort (rknn版本已经Ready)

目录 1. 前言 2. 储备知识 3. 准备工作 4. 代码修改的地方 5.结果展示 1. 前言 之前一直在忙着写文档&#xff0c;之前一直做分类&#xff0c;检测和分割&#xff0c;现在看到跟踪算法&#xff0c;花了几天时间找代码调试&#xff0c;看了看&#xff0c;展示效果比单纯的检…

linux--链表动态创建

头插法&#xff1a; 核心代码&#xff1a; s->next head->next; head->next s; 尾插法 核心代码&#xff1a; tail head; s->next NULL; tail->next s; tail s; 当用头插法依次插入值分别为1,2,3,4,5的结点后&#xff0c; 单链表顺序为&#xff1a; he…