首先,我们会有个疑问,为什么需要 toRef 和 toRefs?
toRef 和 toRefs 是 Vue3 中的响应式转换工具函数,它们的存在主要有以下两点原因:
- 在 Vue 中,直接使用响应式对象的属性可以实现属性的双向绑定和响应式更新。但是有时候需要将某个属性提取出来作为独立的 ref 对象,这样可以在不影响源对象的情况下,对属性进行单独的访问和修改。toRef 函数正是为了解耦属性的关联,将属性转换为一个独立的 ref 对象。
- Vue 的组件系统中,父组件向子组件传递属性时,需要将这些属性声明为响应式对象。但是,如果直接将整个响应式对象传递给子组件,子组件无法通过解构或者直接访问每个属性。这时,toRefs 函数就可以将整个响应式对象转换为一个普通对象,每个属性都是独立的ref对象,子组件可以轻松解构和访问这些属性。
换句话说,toRef 和 toRefs 就是用来创建响应式的引用的,主要用来取出响应式对象里的属性,或者解构响应式对象,解构出来的属性值依然是响应式属性,如果不用 toRef 或者 toRefs,直接解构会丢失响应式效果。
typescript
<script setup>
import { reactive } from 'vue';
let info = reactive({
name: 'Echo',
age: 26,
gender: 'Male',
})
// 这样解构会丢失响应式效果
let { name, age, gender } = info;
</script>
下面,我们一起看看 toRef 和 toRefs 的用法:
1. toRef
- toRef 函数可以将一个响应式对象的属性转换为一个独立的 ref 对象。
- 返回的是一个指向源对象属性的 ref 引用,任何对该引用的修改都会同步到源对象属性上。
- 使用 toRef 时需要传入源对象和属性名作为参数。
下面我们一起看一段代码:
typescript
<script setup>
import { reactive, toRef } from 'vue';
let info = reactive({
name: 'Echo',
age: 26,
})
let age = toRef(info, 'age');
const updateInfoObjAge = () => {
info.age++;
}
const updateAge = () => {
age.value++;
}
</script>
<template>
<div id="app">
<p>info对象中的age:{{ info.age }}</p>
<button @click="updateInfoObjAge">更新info对象中的 age</button>
<br />
<p>使用toRef函数转换后的age:{{ age }}</p>
<button @click="updateAge">更新 age</button>
</div>
</template>
上面这段代码中:
- 我们使用 reactive 创建了一个名为 info 的响应式对象,包含 name 和 age 属性。
- 然后使用 toRef 函数将 info 对象的 age 属性转换为一个独立的 ref 对象。
- 接着定义了两个方法 updateInfoObjAge 和 updateAge,分别用于更新 info 对象的 age 属性和 age 引用的值。
从上面的代码中,我们可以看到,age 属性是使用 toRef 函数转换的具有响应式的 ref 属性,当我们更新时,使用 reactive 定义的响应式对象 info 中的 age 也会随着更新。
而且,从开发工具中,我们也可以看到,age 属性是一个具有 ref 响应式的属性。
2. toRefs
- toRefs 函数可以将一个响应式对象转换为一个普通的对象,该对象的每个属性都是独立的 ref 对象。
- 返回的对象可以进行解构,每个属性都可以像普通的 ref 对象一样访问和修改,而且会保持响应式的关联。
- toRefs 的使用场景主要是在将响应式对象作为属性传递给子组件时,确保子组件可以正确地访问和更新这些属性。
下面我们一起看一段代码:
typescript
<script setup>
import { reactive, toRefs } from 'vue';
let info = reactive({
name: 'Echo',
age: 26,
gender: 'Male',
})
let { name, age, gender } = toRefs(info);
const update = () => {
name.value = 'Julie';
age.value = 33;
gender.value = 'Female';
}
</script>
<template>
<div id="app">
<p>info对象中的name:{{ info.name }}</p>
<p>info对象中的age:{{ info.age }}</p>
<p>info对象中的gender:{{ info.gender }}</p>
<br />
<p>解构出来的name:{{ name }}</p>
<p>解构出来的age:{{ age }}</p>
<p>解构出来的gender:{{ gender }}</p>
<button @click="update">更新数据</button>
</div>
</template>
上面这段代码中:
- 首先,使用 reactive 函数创建了一个响应式对象 info,包含了 name、age 和 gender 三个属性,同时设置了初始值。
- 接着,使用 toRefs 函数将 info 对象转换为多个独立的响应式引用对象。然后通过解构赋值,把 name、age 和 gender 三个响应式引用对象分别赋给了相应的变量。
- 最后,添加了一个按钮,点击按钮会触发 update 函数,在 update 函数中,通过修改响应式引用对象的 value 属性来更新数据的值。
从开发工具中,我们可以看到,解构出来的每个属性,都是独立的具有 ref 响应式的属性,因此,我们需要使用 .value 才能访问和修改其值。
3. 相同点
- toRef 和 toRefs 都用于将响应式对象的属性转换为 ref 对象。
- 转换后的属性仍然保持响应式,对属性的修改会反映到源对象上。
- 不管是使用 toRef 还是 toRefs 将响应式对象转成普通对象,在 script 中修改和访问其值都需要通过 .value 进行。
4. 不同点
- toRef 修改的是对象的某个属性,生成一个单独的 ref 对象。
- toRefs 修改的是整个对象,生成多个独立的 ref 对象集合。
- toRefs 适用于在组件传递属性或解构时使用,更加方便灵活,而 toRef 更适合提取单个属性进行操作。
通过上面对 toRef 和 toRefs 函数的了解,你们知道为什么需要 toRef 和 toRefs 了吗?
总结起来就是:在不丢失响应式的前提下,对响应式对象数据进行解构,这样如果在 setup 中返回 toRefs(obj),或者 toRef(obj, 'xxx'),我们就可以在 template 中不使用 obj.xxx 来取值了。