您现在的位置是:首页 >技术交流 >VUE3浅析---响应式网站首页技术交流

VUE3浅析---响应式

夜间沐水人 2024-07-13 06:01:02
简介VUE3浅析---响应式

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



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: string
	age: 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 = 18
	User1.value.age = 18
	User2.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 = 18
	triggerRef(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的响应式原理

后续补充……

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。