VUE3浅析---响应式

news/2025/2/12 8:06:20/

VUE3中setup语法糖解决响应式的方案,所有的只要被ref或者reactive包裹的变量,都会转变成响应式。而在VUE2中,要想做成响应式,必须将变量定义在data函数中。


文章目录

    • 1、ref:将一个属性或者对象定义成ref对象,也就是将一个属性或者对象变成响应式,修改值必须`.value`才能处理对应的值。
    • 2、isRef:用来判断一个属性或者对象是不是ref对象。
    • 3、shallowRef:和ref的作用相似,但是shallowRef只能用来做浅层响应式
    • 4、triggerRef:强制收集所有的改变,和shallowRef一起使用,将shallowRef也变成深层次相应,即得到和ref一样的效果,但是ref和shallowRef不要一起使用,因为ref底层会调用triggerRef,会导致shallowRef的值也会被强制更新
    • 5、customRef:自己实现ref的逻辑,在实现的过程中可以自己增加其他额外的逻辑处理。
    • 6、reactive:将一个对象变成响应式,修改值必须`.属性`即可处理对应的值
    • 7、shallowReactive:也是将一个对象变成浅层响应式,即只能处理对象的第二层属性的值,和shallowRef一样的特性。
    • 8、readonly:将reactive对象变成只读对象,该只读对象不允许再次被赋值等操作,但是该只读对象值依旧受原始对象值的影响。
    • 9、reactive和ref的区别
    • 10、VUE的响应式原理


1、ref:将一个属性或者对象定义成ref对象,也就是将一个属性或者对象变成响应式,修改值必须.value才能处理对应的值。

  • 以下代码定义了三个User对象,并且都是使用ref做成了响应式,当点击按钮改变User对象的值的时候,页面上的值也会被改变,这就是响应式的作用。
  • 使用ref获取dom元素。
import { ref, onMounted } from 'vue'
import type { Ref } from 'vue' // Ref是一个类型定义,类型定义导入的时候必须使用type关键字// 定义User的各个属性的字段类型
type UserType = {name: stringage: number
}// 三种不同的User对象的定义
const User = ref<UserType>({ name: '小明', age: 12 })
const User1: Ref<UserType> = ref({ name: '小明', age: 12 })
const User2 = ref({ name: '小明', age: 12 })const refTest = () => {	User.value.age = 18User1.value.age = 18User2.value.age = 18
}// 使用ref获取dom元素
const dom = ref<HTMLElement>()
onMounted(() => {console.log(dom.value?.innerHTML) // onMounted结束之后,才能获取到dom元素,所以需要放在onMounted中才能获取到dom
})<button @click="refTest" style="height: 100px; width: 100px; background: green">refTest</button>
<div><p>User: {{ User }}</p><p>User1: {{ User1 }}</p><p>User2: {{ User2 }}</p>
</div><div ref="dom">通过ref获取dom</div>

2、isRef:用来判断一个属性或者对象是不是ref对象。

isRef实际上在项目中很少使用,然而在ref源码中很多地方都在使用

import { ref, isRef } from 'vue'
const a = ref<number>(1)
const b = 1
console.log('a是ref对象:', isRef(a))
console.log('b是ref对象:', isRef(b))a是ref对象: true
b是ref对象: false

3、shallowRef:和ref的作用相似,但是shallowRef只能用来做浅层响应式

shallowRef只能用来做浅层响应式,也就是说他只能做到修改到.value的这一层,.value后边的数据他不能响应式的修改。

  • 当我们点击shallowRefTest按钮时,UserE2在页面上的输出是没有任何变化的,但是如果我们在控制台上去看UserE2对象的时候会发现,实际上他的值已经改变了,但是不能渲染到页面上。
  • 当我们点击shallowRefTest1按钮时,UserE2在页面上的输出发生了变化,这是因为UserE1的处理是正确的,触发了整个ref的机制,导致UserE2的值也被改变了,并正确的渲染到了界面上。
  • 当我们点击shallowRefTest2按钮时,UserE2、UserE1在页面上的输出发生了变化,这是因为UserE1的处理是正确的,shallowRef的响应式处理只能从.value后边修改。
  • 当我们点击shallowRefTest3按钮时,UserE2、UserE1在页面上的输出发生了变化,这是因为ref的改变的会影响shallowRef的改变,也就是说,shallowRef和ref的处理放一起的时候会被ref影响,shallowRef也会变成深响应式,原因是ref底层会调用triggerRef,而triggerRef会强制收集所有的改变,进而导致shallowRef深层次的改变。
import { ref, shallowRef, triggerRef } from 'vue'
const a = ref<number>(1)
const UserE1 = shallowRef({ name: "小明", age: 12 });
const UserE2 = shallowRef({ name: "小明", age: 12 });
const shallowRefTest = () => {UserE2.value.age = 18;
};const shallowRefTest1 = () => {UserE2.value.age = 18;UserE1.value = {name: "小明1",age: 121,};
};const shallowRefTest2 = () => {UserE2.value = 18;UserE1.value = {name: "小明1",age: 121,};
};const shallowRefTest3 = () => {a.value = 10;UserE2.value = 18;
};<button @click="shallowRefTest" style="height: 100px; width: 100px; background: green">shallowRefTest</button>
<button @click="shallowRefTest1" style="height: 100px; width: 100px; background: green">shallowRefTest1</button>
<button @click="shallowRefTest2" style="height: 100px; width: 100px; background: green">shallowRefTest2</button>
<button @click="shallowRefTest3" style="height: 100px; width: 100px; background: green">shallowRefTest3</button>
<div><p>UserE1: {{ UserE1 }}</p><p>UserE2: {{ UserE2 }}</p>
</div>

4、triggerRef:强制收集所有的改变,和shallowRef一起使用,将shallowRef也变成深层次相应,即得到和ref一样的效果,但是ref和shallowRef不要一起使用,因为ref底层会调用triggerRef,会导致shallowRef的值也会被强制更新

  • 当我们点击shallowRefTest4按钮时,如果我们不加triggerRef(UserE2),UserE2在页面上的输出不会发生变化,如果我们加triggerRef(UserE2),UserE2在页面上的输出会发生变化,shallowRef也会变成深响应式,原因是triggerRef会强制收集所有的改变,进而导致shallowRef深层次的改变。
import { shallowRef, triggerRef } from 'vue'
const UserE2 = shallowRef({ name: "小明", age: 12 });const shallowRefTest4 = () => {UserE2.value.age = 18triggerRef(UserE2) // 主动调用,触发更新操作
}<button @click="shallowRefTest4" style="height: 100px; width: 100px; background: green">shallowRefTest4</button>
<div><p>UserE2: {{ UserE2 }}</p>
</div>

5、customRef:自己实现ref的逻辑,在实现的过程中可以自己增加其他额外的逻辑处理。

customRef允许我们自己实现ref的逻辑,并增加一些额外的处理,它的实现逻辑主要依赖于get和set方法。比如我们在set的时候需要从后台获取的,假设我们一次调用了一百次后台,但是获取的都是同一个值,那这样我们可以在set方法中使用setTimeOut进行防抖处理,避免服务器压力过大。

import { customRef } from 'vue'
const myRefTest = MyRef<string>('myRefTest')
const myRefChange = () => {myRefTest.value = 'myRefTest:我自己实现了ref'
}function MyRef<T>(value: T) {return customRef((track, triggeer) => {return {get() {track();return value;},set(newValue) {value = newValue;triggeer();},};});
}<button @click="myRefChange" style="height: 100px; width: 100px; background: green">myRefChange</button>
<div><p>myRefTest: {{ myRefTest }}</p>
</div>

6、reactive:将一个对象变成响应式,修改值必须.属性即可处理对应的值

  • 以下代码定义了一个UserE3对象,并且都是使用reactive做成了响应式,当点击按钮改变UserE3对象的值的时候,页面上的值也会被改变,也达到了响应式的作用。
  • reactive定义对象,因为在reactive的定义中,约束了传入的值只能为Object
import { reactive } from 'vue'
// 定义属性约束
type UserType = {name: string;age: number;
};
// 定义一个reactive的对象
const UserE3 = reactive<UserType>({ name: "小明", age: 12 });
const shallowRefTest5 = () => {UserE3.age = 18;
};<button @click="shallowRefTest5" style="height: 100px; width: 100px; background: green">shallowRefTest5</button>
<div><p>UserE3: {{ UserE3 }}</p>
</div>

7、shallowReactive:也是将一个对象变成浅层响应式,即只能处理对象的第二层属性的值,和shallowRef一样的特性。

import { shallowReactive } from 'vue'
const shallowReactiveE4 = shallowReactive<any>({foot: {bar: {name: "bar",},},
});
const shallowRefTest8 = () => {// shallowReactiveE4.foot.bar.name = 22 // 这里实际上不能修改name的值,只能处理对象的第二层属性的值,即foot这一层shallowReactiveE4.foot = 22; // 这里实际上能修改foot的值
};<button@click="shallowRefTest8"style="height: 100px; width: 100px; background: green"
>shallowRefTest8
</button>
<div><p>shallowReactiveE4: {{ shallowReactiveE4 }}</p>
</div>

8、readonly:将reactive对象变成只读对象,该只读对象不允许再次被赋值等操作,但是该只读对象值依旧受原始对象值的影响。

import { reactive, readonly } from 'vue'
const readonlyUserE3 = readonly(UserE3);
const shallowRefTest7 = () => {readonlyUserE3.age = 22; // 这里直接对readonlyUserE3操作不会改变readonlyUserE3对象属性// UserE3.age = 18  // 如果这里改变的是原始对象,readonlyUserE3也会受影响
};<button@click="shallowRefTest7"style="height: 100px; width: 100px; background: green"
>shallowRefTest7
</button>
<div><p>readonlyUserE3: {{ readonlyUserE3 }}</p>
</div>

9、reactive和ref的区别

  • reactive只能定义Object类型的值作为响应式,比如:Map,List等,而ref可以将任意值变成响应式,包括对象,字符串,数字等。
  • reactive在处理值的时候直接.属性即可处理对应的值,而ref需要.value才能处理
  • reactive在异步场景下不能直接赋值,否则会破坏他的proxy代理对象,从而无法变成响应式。解决办法为:
    • 将data解构并使用push方法进行添加
    • 定义类似于list1的对象,对象里面在放arr数组属性,然后在使用=将data直接复制给list1对象里面的arr数组属性,使用的时候也是list1.arr
let list = reactive<string[]>([]);
let list1 = reactive<{arr: string[];
}>({arr: [],
});
const shallowRefTest6 = () => {setTimeout(() => {const data = ["data1", "data2", "data3", "data4", "data5"];// list = data  // 直接用等号赋值会发现,list实际上已经有值了,并且在控制太也能看到,但是页面没有渲染,这就说明:=会破坏响应式list.push(...data); // 解决办法1:就是将data解构并使用push方法进行添加list1.arr = data // 解决办法2:使用对象,将数组变为对象的一个属性,并直接赋值。console.log(list);}, 1000);
};<button @click="shallowRefTest6" style="height: 100px; width: 100px; background: green">shallowRefTest6</button>
<div><ul><li v-for="item in list" :key="item">{{ item }}</li><li v-for="item in list1.arr" :key="item">list1 + {{ item }}</li></ul>
</div>

10、VUE的响应式原理

后续补充……


http://www.ppmy.cn/news/109640.html

相关文章

C语言杂七杂八

fget函数的用法 fgets() 函数用来从指定的文件中读取一个字符串&#xff0c;并保存到字符数组中。 str 为字符数组&#xff0c;n 为要读取的字符数目&#xff0c;fp 为文件指针。 char *fgets(char *str, int n, FILE *stream) 返回值&#xff1a;读取成功时返回字符数组首地…

Ceres简介及示例(10)On Derivatives(Automatic Derivatives)

现在我们将讨论自动微分算法。它是一种可以快速计算精确导数的算法&#xff0c;同时用户只要做与数值微分法类似的工作。下面的代码片段实现了对Rat43的CostFunction。 struct Rat43CostFunctor {Rat43CostFunctor(const double x, const double y) : x_(x), y_(y) {}template…

象棋游戏(文案)

绿王和红王的战斗规则绿王和红王的战斗规则刘邦和项羽&#xff0c;楚河汉界&#xff0c;红王和绿王的象棋游戏世界。  方案规则&#xff1b;  1、楚河汉界中有一口水井&#xff0c;这是战场的最后归宿&#xff0c;气血值是由这一口水井进行分配的。&#xff08;地图造型水井…

代码随想录算法训练营第三十六天|435. 无重叠区间 763.划分字母区间 56. 合并区间

目录 LeeCode 435. 无重叠区间 LeeCode 763.划分字母区间 LeeCode 56. 合并区间 LeeCode 435. 无重叠区间 435. 无重叠区间 - 力扣&#xff08;LeetCode&#xff09; 思路1&#xff1a;按照右边界排序&#xff0c;从左向右记录非交叉区间的个数。最后用区间总数减去非交叉…

springboot整合kafka入门

kafka基本概念 producer&#xff1a; 生产者&#xff0c;负责发布消息到kafka cluster(kafka集群)中。生产者可以是web前端产生的page view&#xff0c;或者是服务器日志&#xff0c;系统CPU、memory等。 consumer&#xff1a; 消费者&#xff0c;每个consumer属于一个特定的c…

桶排序 — 计数排序和基数排序

计数排序 int类型数组&#xff0c;其中存的是员工的年龄。比如说16 - 150。对于这样的数据来讲&#xff0c;数据状况是受限的。此时如果将数组从小到大进行排序&#xff0c;该如果实现&#xff1f; 这个实现很简单&#xff0c;实现一个统计数组范围从 0 ~ 150&#xff0c;遍历原…

【文章学习系列之模型】SCALEFORMER

本章内容 文章概况模型结构主要方法多尺度框架跨尺度标准化模型输入编码损失函数 实验结果消融实验跨尺度标准化自适应损失函数 总结 文章概况 《SCALEFORMER: ITERATIVE MULTI-SCALE REFINING TRANSFORMERS FOR TIME SERIES FORECASTING》是2023年发表于ICLR上的一篇论文。作…

陕西发布!陕西省重点实验室申报条件类别、认定程序要求

本文整理了陕西省重点实验室申报条件&#xff0c;认定材料等相关内容&#xff0c;感兴趣的朋友快跟小编一起来看看吧&#xff01; 一、总体思路 本次省重点实验室布局建设工作以填补我省优势学科领域下无省级及以上科学与工程研究类科技创新基地的空白为主,同时兼顾前沿、新兴、…