Vue3-响应式函数-ref和reactive函数的使用
一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第1天,点击查看活动详情。
1. 声明响应式数据
在Vue3中,通过
ref()
和reactive()
函数来声明响应式数据。注意:
ref()
可以声明任意类型的响应式数据。reactive()
只能声明对象(数组)类型的响应式数据。
let tom: Person = {
age: 30,
name: "cat"
}
//1。通过ref声明响应式数据
const ref1 = ref<number>(1);//基础类型数据
const ref2 = ref<Person>(tom); //对象/数组类型数据
//2. 通过reactive声明相适应数据
const number = reactive(1);//报错不能声明基本类型的数据,只能传入一个对象。
const reactive1 = reactive(tom);//可以
2. reactive()函数
reactive()
函数内部通过New Proxy()
的方式来对数据进行代理。从而实现响应式。由于Proxy的限制,只能用来创建对象类型的响应式数据。通过
reactive()
函数,会得到一个代理对象(也叫响应式对象),只有通过代理对象操作属性,才是响应式的,通过原对象操作属性是不会响应。
let tom: Person = {
age: 30,
name: "cat"
}
const reactive1 = reactive(tom); //返回代理对象。(也叫响应式对象)
reactive1.name = "cat";//操作代理对象,响应式。
tom.name = "mi"; //操作原对象,不是响应式。
2.1 reactive的单例模式:
reactive()
函数的设计是单例模式,也就是说:对同一个对象多次调用reactive()
函数,返回的都是同一个代理对象。对一个代理对象调用reactive()
函数,总会返回代理对象自身。这个规则对嵌套对象也适用。依靠深层响应性,响应式对象内的嵌套对象依然是代理对象。
let tom: Person = {
age: 30,
name: "cat"
}
const reactive1 = reactive(tom);
const reactive2 = reactive(tom);
const reactive3 = reactive(reactive2);
console.log(reactive1 == reactive2); //true
console.log(reactive3 == reactive2); //true
console.log(reactive3 == reactive1); //true
2.2 reactive的深层代理
reactive()
是深层次的代理,响应式对象内的嵌套对象依然是代理对象。并且也是单例模式。(Proxy
是浅代理,对于嵌套的对象的不代理的)
let tom: Person = {
age: 30,
name: "cat",
sex: {
ho: "02",
sex: "男"
},
}
const reactive1 = reactive(tom);
console.log(reactive1.sex); //通过打印即可验证,打印出来的是一个响应式对象
2.2 reactive()
函数的局限性
-
只能处理对象类型:
- 由于
Proxy
的限制,reactive()
函数只对对象类型的数据有效,对于基本数据类型的代理是无效的。
- 由于
-
不能更改响应式对象引用:
- 因为 Vue 的响应式系统是通过 属性 访问进行追踪的,因此我们必须始终保持对该
响应式对象的相同引用
。(Vue官网解释) - 意思是:将响应式对象的变量赋值给另一个对象,丢失响应式。(修改嵌套的响应式对象里面嵌套对象引用可以,因为是深层代理。)
- (通过ref函数生成的可以,)后面介绍。
let tom: Person = { age: 30, name: "cat" } let reactive1 = reactive(tom); reactive1 = { age: 0, name: "cat" }//将响应式对象的变量赋值给另一个对象,丢失响应式 //修改嵌套响应式对象里面嵌套的对象引用可以,因为是深层代理。 let tom: Person = { age: 30, name: "tom", sex: { ho: "02", sex: "男" }, } let reactive1 = reactive(tom); reactive1.sex = { ho: "03", sex: "女" }
- 因为 Vue 的响应式系统是通过 属性 访问进行追踪的,因此我们必须始终保持对该
-
属性赋值失去响应式:
- 当我们将响应式对象的
属性
(赋值给另一个变量、使用解构赋值、将属性传入一个函数中),我们会失去响应性。 - 只对基本数据类型有限制。如果属性的类型是对象类型,则不受限制。
- 原因:基本数据类型是值传递,而对象类型是引用传递。
-
因为值传递的本意是复制值,就是简单的复制一个值给另一个变量。
-
而对象类型的数据不会失去响应式是因为:1.对象是引用传递,复制的是对象的引用。2.在进行代理的时候是深层次的代理,因此该对象自身就是响应式对象。
-
let tom: Person = { age: 30, name: "cat" } let reactive1 = reactive(tom); let ng: string = reactive1.name; // 将属性赋值给另一个变量,ng变量不是响应式。 let {age, name} = reactive1; // 使用解构赋值,得到的属性不是响应式。 setName(reactive1.name);//将属性传入函数中,该属性不是响应式 function setName(name: string) { name = "cat02" } //属性是一个对象类型。 let tom: Person = { age: 30, name: "cat", sex: { ho: "02", sex: "男" }, } let reactive1 = reactive(tom); setName(reactive1.sex) function setName(sex: Sex) { sex.sex = "女";//可以修改,是响应式。 } let {sex: sex1} = reactive1; sex1.ho = "sssss";//可以修改,是响应式。
- 当我们将响应式对象的
3. ref()
函数
为了解决
reactive()
带来的限制,Vue 也提供了一个ref()
方法来允许我们创建可以使用任何值类型的响应式 ref对象。通过
ref()
函数创建响应式的数据,会返回一个RefImpl
对象(也叫ref对象
),响应式数据会被封装到ref对象的.value
属性中。(.value
属性就是响应式属性)
如果是基本类型的数据,那么ref
函数就会使用Object.defineProperty()
来实现对数据的劫持。
如果是对象(数组)类型的数据,则ref函数就会调用reactive()
函数。而.value
就是代理对象
let tom: Person = {
age: 30,
name: "tom",
}
const ref1 = ref(1);//基本类型
console.log(ref1.value);
const ref2 = ref<Person>(tom);//对象类型
console.log(ref2.value); //可以看到时一个代理对象
使用ref()
函数创建响应式的数据,是可以更改对象的引用的。(通过reactive函数的是不能替换)
const person = ref<Person>({
age: 20,
name: "tom"
});
let tom: Person = {
age: 30,
name: "cat"
}
person.value = tom;//可以替换,因为当值发表改变了就会调用reactive函数。
一言以蔽之,ref()
使我们能创造一种任意值的 “引用” 并能够不丢失响应性地随意传递。这个功能非常重要,因为它经常用于将逻辑提取到 组合函数 中。(Vue官网)
4. reactive 和 ref的区别
reactive
只能用来创建对象类型的响应式数据,而ref
则是可以创建任意数据类型的响应式对象。reactive
创建的是proxy
的代理对象,而ref创建的则是ref对象。- 通过
reactive
创建的响应式对象是不能更换对象的引用的,而通过ref
创建响应式对象是可以替换的。 reactive
是通过Proxy来对数据进行代理的,而ref
,如果是基本数据类型,通过Object.defineProperty()
来实现对数据的劫持。如果是对象类型的数据,则是通过调用reactive
函数实现。