文章目录
前言
上一篇学习了 ref 全家桶,在此基础上一起学习下 reactive 全家桶
一、reactive 对比 ref
ref
可以接收 所有类型,reactive
只能接收 `object类型(array、object、Map、Set)ref
在取值和赋值时都要通过.value
,reactive
不需要reactive 不能直接整体赋值
,因为它是通过proxy
创建的响应式对象,直接赋值会 破坏响应式- 解决方案 1:可以 调用一些原生操作,比如 数组可用 push、pop 等去修改
- 解决方案 2:外面再包上一层
二、直接整体赋值会破坏响应式
import { reactive } from "vue";
let arr = reactive<number[]>([]);
setTimeout(() => {
arr = [1, 2, 3];
console.log(arr); // 数据变化了,但模板没变化
}, 100);
2.1. 解决方法一:使用push方法
import { reactive } from "vue";
let arr = reactive<number[]>([]);
setTimeout(() => {
const newArr = [1, 2, 3];
arr.push(...newArr);
console.log(arr);
}, 100);
2.2. 解决方法二:将数组定义为对象中的一个属性
import { reactive } from "vue";
type T = {
arr: Array<number>;
};
let list = reactive<T>({
arr: [],
});
setTimeout(() => {
list.arr = [1, 2, 3];
console.log(list.arr);
}, 100);
三、将 reactive 的值 设置为 readonly
- 设置为 readonly 后不可修改
- 修改 reactive 源数据,readonly 的值也会受影响
<script setup lang="ts">
import { reactive, readonly } from "vue";
const info = reactive({
name: "xiaoman",
});
const rd = readonly(info);
// 设置为 readonly 后不可修改
// rd.name = 'daman'
// 但是可以修改它的源数据
info.name = "blue";
console.log("修改源数据,readonly的值也会受影响", info, rd);
</script>
- 可以看到,两者的值都被修改了:
四、shallowReactive
与上一篇 ref 全家桶 中 shallowRef
十分相似,都是浅层的响应式
修改第一层会触发视图更新,修改深层不会触发视图更新,有其他触发视图更新的操作,也会顺带将其更新(官方回复这并不是一个bug,而是特色)
4.1. reactive 是深层的响应式,视图会立刻更新
import { reactive } from "vue";
const obj2 = reactive({
foo: {
bar: {
name: "xiaoman",
},
},
});
setTimeout(() => {
obj2.foo.bar.name = "blue222222222";
}, 1000);
4.2. shallowReactive 是浅层的响应式
const obj3 = shallowReactive({
foo: {
bar: {
name: "xiaoman",
},
},
});
- 修改浅层才会触发响应式,会立刻更新:
setTimeout(() => {
obj3.foo = {
bar: {
name: "小满",
},
};
}, 1000);
- 修改深层可以打印到新值,但视图不会立刻更新:
setTimeout(() => {
// 修改深层不会触发更新
obj3.foo.bar.name = "blue3333333333";
console.log("obj3", obj3); // 打印成功,但是视图不会更新
}, 1000);
4.3. 会被其他有视图更新的操作,顺带着一起更新掉
import { reactive, shallowReactive } from "vue";
const obj2 = reactive({
foo: {
bar: {
name: "xiaoman",
},
},
});
const obj3 = shallowReactive({
foo: {
bar: {
name: "xiaoman",
},
},
});
setTimeout(() => {
// 修改深层不会触发更新
obj3.foo.bar.name = "blue3333333333";
console.log("obj3", obj3);
obj2.foo.bar.name = "blue555555"; // obj2 是 reactive,触发更新视图
}, 1000);
五、源码学习
主要关注 createReactiveObject 函数的逻辑
5.2. 学习笔记
/**
*
* 1、reactivity.d.ts中,关于 reactive 的类型定义:
*
* export declare function reactive<T extends object>(target: T): UnwrapNestedRefs<T>;
*
* 其中 T extends object:即只能传入 object 的子类型
*
* 2、reactivity.cjs.prod.js中,
*
* function reactive(target) {
if (isReadonly(target)) {
return target;
}
.....
* 即:reactive 如果传入只读类型的,会直接返回
*
* 3、createReactiveObject 函数中,
* - 若传入的不是 object 类型,会抛出警告(dev环境)并直接返回;
* - 若传入的是被 proxy 代理过的(并且不是为了将其变为只读),也会直接返回
* - 如果能从缓存中找到,则直接返回(weakMap)
* - 如果在白名单中,也会直接返回,例如 __skip__(后面会讲的 markRaw 处理过的会加一个 __skip__,会跳过proxy代理 )
* - 如果以上条件都没触发,就会进行 proxy代理
*
*/