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

您所在的位置:网站首页 js对象赋值可以不写属性名吗 vue3 ref的使用、问题及源码分析;引用型变量和原始类型变量的复制值

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

2024-07-02 20:59| 来源: 网络整理| 查看: 265

文章目录 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 = (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数组

{{ value }} {{ value2 }} 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" } ];

在子组件中接收:

import { ref, computed } from "vue"; import { ColumnProps } from "@/components/ProTable/interface"; const props = defineProps(); 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);

使用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的单向数据流原则?



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3