『引言』
上一篇文章中了解到ref()和reactive()的使用方法等等相关的问题。其中在上一篇文章的末尾提到了toRefs(),不知道是否还记得😎❓这篇就会介绍到它和一个跟它很像,但写法就差一个字母的toRef()。
『toRef()』
『定义』
【官方解释】可以将值、refs 或 getters 规范化为 refs (3.3+)。
也可以基于响应式对象上的一个属性,创建一个对应的 ref。这样创建的 ref 与其源属性保持同步:改变源属性的值将更新 ref 的值,反之亦然。
【我的理解】将某个对象中的值转化为响应式数据,它会接受两个参数。
toRef()返回一个ObjectRefImpl对象。
『用法』
const 变量名 = toRef(源对象,源对象下的某个属性)
const result = toRef(target, 'key')
『注意』
toRef()只能处理一个属性。result是否具有响应式,取决于target是否具有响应式。
『示例🌰』
这个示例还是ref()这个效果。
<template>
<div>
<h1>人物简介</h1>
<p>姓名:{{nameRef}}</p>
<p>年龄:{{ageRef}}岁</p>
<p>爱好:{{hobbiesRef.join('、')}}</p>
<p>地址:{{proviceRef}} - {{cityRef}}</p>
<p>描述:{{descriptionRef}}</p>
<button @click="changeReactive">
修改信息
</button>
</div>
</template>
<script setup>
import { reactive, toRef } from 'vue'
const data = reactive ({
name: 'pupu',
age: 10,
hobbies: ['唱歌', '画画'],
address: {
provice: '浙江省',
city: '杭州市'
},
description: '一点也不可爱,不喜欢吃蜂蜜!'
})
// 通过toRef创建一个Ref响应式
const nameRef = toRef(data, 'name')
const ageRef = toRef(data, 'age')
const hobbiesRef = toRef(data, 'hobbies')
const proviceRef = toRef(data.address, 'provice')
const cityRef = toRef(data.address, 'city')
const descriptionRef = toRef(data, 'description')
// 经过了toRef的处理,修改变量的值,那么就需要xx.value
const changeReactive = () => {
nameRef.value = 'wnxx'
ageRef.value = 3
hobbiesRef.value = ['打羽毛球', '旅游']
proviceRef.value = '云南省'
cityRef.value = '丽江市'
descriptionRef.value = '非常的可爱,特别喜欢吃蜂蜜!'
// 打印toRef的处理之后的数据
console.log(
nameRef,
"nameRef",
ageRef,
"ageRef",
hobbiesRef,
"hobbiesRef",
proviceRef,
"proviceRef",
cityRef,
"cityRef",
descriptionRef,
"descriptionRef"
)
// 打印toRef的处理之前的数据
console.log(
data.name,"data.name",
data.age,"data.age",
data.hobbies,"data.hobbies",
data.address.provice,"data.address.provice",
data.address.city,"data.address.city",
data.description,"data.description"
)
}
</script>
上图就是,我们通过在控制台打印的信息。我们可以通过这个打印信息看到,经过toRef转换后的数据和未使用toRef之前的数据。
『toRefs()』
『定义』
【官方解释】将一个响应式对象转换为一个普通对象,这个普通对象的每个属性都是指向源对象相应属性的 ref。每个单独的 ref 都是使用 toRef() 创建的。
【我的理解】将响应式的对象变为普通对象,再解构,在模板中就可以直接使用属性,不用data.name。
toRefs()相当于一个中间件,进行转换,处理整个对象,但是只能处理第一层。
toRefs()返回值是一个对象,这个对象里面每个key的值是ObjectRefImpl对象。
『用法』
const result = toRefs(源对象)
const result = toRefs(target)
『注意』
toRefs(源对象),却可以一次性批量处理。target的每个属性都会被转换为toRef 对象。toRefs转变为普通对象后,不会丢失响应式。result是否具有响应式,取决于target是否具有响应式。
『示例🌰』
实现的效果和toRef()一样。
<template>
<div>
<h1>人物简介</h1>
<p>姓名:{{name}}</p>
<p>年龄:{{age}}岁</p>
<p>爱好:{{hobbies.join('、')}}</p>
<p>地址:{{addressprovice}} - {{addresscity}}</p>
<p>描述:{{description}}</p>
<button @click="changeReactive">
修改信息
</button>
</div>
</template>
<script setup>
import { reactive, toRefs } from 'vue'
const data = reactive ({
name: 'pupu',
age: 10,
hobbies: ['唱歌', '画画'],
addressprovice: '浙江省',
addresscity: '杭州市',
description: '一点也不可爱,不喜欢吃蜂蜜!'
})
// 打印toRefs之前的数据
console.log(data, 'data')
// 通过toRefs创建一个Ref响应式
const {
name,
age,
hobbies,
addressprovice,
addresscity,
description
} = toRefs(data)
// 经过了toRefs的处理,修改变量的值,那么就需要xx.value
const changeReactive = () => {
name.value = 'wnxx'
age.value = 3
hobbies.value = ['打羽毛球', '旅游']
addressprovice.value = '云南省'
addresscity.value = '丽江市'
description.value = '非常的可爱,特别喜欢吃蜂蜜!'
// 打印toRefs之后的数据
console.log(name, age,hobbies,addressprovice,addresscity,description, 'toRefsdata')
}
</script>
可以看得出来转为普通对象的
响应式并未丢失。
👇这里再附上另外不同的写法。
<template>
<div>
<h1>人物简介</h1>
<p>姓名:{{toRefsdata.name.value}}</p>
<p>年龄:{{toRefsdata.age.value}}岁</p>
<p>爱好:{{toRefsdata.hobbies.value.join('、')}}</p>
<p>地址:{{toRefsdata.addressprovice.value}} - {{toRefsdata.addresscity.value}}</p>
<p>描述:{{toRefsdata.description.value}}</p>
<button @click="changeReactive">
修改信息
</button>
</div>
</template>
<script setup>
import { reactive, toRefs } from 'vue'
const data = reactive ({
name: 'pupu',
age: 10,
hobbies: ['唱歌', '画画'],
addressprovice: '浙江省',
addresscity: '杭州市',
description: '一点也不可爱,不喜欢吃蜂蜜!'
})
// 打印toRefs之前的数据
console.log(data, 'data')
// 通过toRefs创建一个Ref响应式
const toRefsdata = toRefs(data)
// 打印toRefs之后的数据
console.log(toRefsdata, 'toRefsdata')
// 经过了toRefs的处理,修改变量的值,那么就需要xx.value
const changeReactive = () => {
toRefsdata.name.value = 'wnxx'
toRefsdata.age.value = 3
toRefsdata.hobbies.value = ['打羽毛球', '旅游']
toRefsdata.addressprovice.value = '云南省'
toRefsdata.addresscity.value = '丽江市'
toRefsdata.description.value = '非常的可爱,特别喜欢吃蜂蜜!'
}
</script>
在这段代码中,我们主要看的是toRefs之后的返回值,里面每个key的值是ObjectRefImpl对象。
『toRefs源码』
toRefs源码如下⬇️:
export function toRefs<T extends object>(object: T): ToRefs<T> {
if (__DEV__ && !isProxy(object)) {
console.warn(`toRefs() expects a reactive object but received a plain one.`)
}
const ret: any = isArray(object) ? new Array(object.length) : {}
for (const key in object) {
ret[key] = propertyToRef(object, key)
}
return ret
}
到这里,toRef()和toRefs()基础的用法、注意点等都介绍的差不多了。
这里我想问一个问题那就是为什么会需要使用这两个‘好朋友’【toRef()和toRefs()】呢❓
『为什么会使用toRef()和toRefs()』
关于为什么会使用toRef()和toRefs()的原因,在上一篇文章我们介绍reactive()的时候,提到过reactive()数据进行解构时,会出现失去响应性的问题。
我想认真观看完上一篇文章以及这篇文章的小伙伴一定会说那原因就是我们要使数据不丢失掉它的响应式。在数据没有丢失响应式的情况下,这样我们可以对数据进行解构操作。
没错就是这个样😌。
那我们也知道...解构对象属于浅拷贝,reactive()数据进行解构时,使用toRef()和toRefs()对数据进行转换,其实就是复制reactive()里面的属性,然后转变ref对象,当我们要对里面的数据进行修改的时候,不仅视图会发生更新,reactive()里面的属性也会更新。
我们简单一点的理解就是,这里面的复制本质是引用 + 响应式ref。这样说的话,使用toRef()和toRefs()同时满足保留了响应式也保留了引用。它们是一种延续响应式。
『toRef()和toRefs()对比』
toRef、toRefs的本质是引用,修改响应式数据,会影响到原始数据,视图会更新。
废话不多说,直接总结。
【相同点:】接收的对象都是响应式对象。
【不同点:】
- toRef()接收两个参数;toRefs()接收一个参数。
- toRef()返回值是一个ObjectRefImpl对象;toRefs()返回值是一个对象,这个对象里面每个key的值是ObjectRefImpl对象。
- toRef()只能处理一个属性;toRefs()可以一次性批量处理。