前言
直接解构响应式对象的属性进行赋值给新的变量,会导致新变量失去响应式。
当修改新变量的值时,不会触发原始响应式对象的更新,从而在模板中也不会有相应的视图更新。
示例:
<template>
<div>
<p>姓名: {{ person.name }}</p>
<p>年龄: {{ person.age }}</p>
<button @click="changeName">修改名字</button>
<button @click="changeAge">修改年龄</button>
</div>
</template>
<script setup lang="ts">
import { reactive } from 'vue';
// person是响应式对象
const person = reactive({
name: '张三', // person.name 是响应式的
age: 36 // person.age 是响应式的
});
// person.name = '李四' 会触发响应式更新
// person.age = 24 会触发响应式更新
// 解构赋值
let { name, age } = person
// 语句相当于执行:let name = person.name; let age = person.age
// 修改name、age,person的name属性、age属性不会改变
const changeName = () => {
name += '哈' // name改变了,没有触发响应式更新
console.log(`name: ${name}, person.name: ${person.name}`);
}
const changeAge = () => {
age ++ // age改变了,没有触发响应式更新
console.log(`age: ${age}, person.age: ${person.age}`)
}
</script>
控制台打印结果:
let { name, age } = person
相当于执行let name = person.name; let age = person.age
,使用方法changeName
、changeAge
修改name
、age
,本质上修改的是使用let
声明的name
变量、age
变量,person
的name
属性、age
属性不会改变。因此不会触发响应式更新。
从响应式的数据person
直接解构出来的name
、age
不是响应式的。
在 Vue 3 中,toRefs()
和toRef()
都是用于处理响应式对象的工具函数,它们的作用是将响应式对象中的属性转换为独立的响应式引用。主要用来取出响应式对象里的属性,或者解构响应式对象。
toRefs()
toRefs()
将一个响应式对象转换为一个普通对象,这个普通对象的每个属性都是指向源对象相应属性的 ref
。每个单独的 ref
都是使用 toRef()
创建的。
使用toRefs()
函数来保持解构后的属性的响应式:
<template>
<div>
<p>姓名: {{ person.name }}</p>
<p>年龄: {{ person.age }}</p>
<button @click="changeName">修改名字</button>
<button @click="changeAge">修改年龄</button>
</div>
</template>
<script setup lang="ts">
import { reactive, toRefs } from 'vue';
const person = reactive({
name: '张三',
age: 36
});
// 将响应式对象解构为响应式引用
let { name, age } = toRefs(person)
// 使用toRefs解构后,name、age都变成了使用ref()定义的响应式引用,指向原始响应式对象中的属性。
console.log('toRefs(person):', toRefs(person))
console.log('解构后的name:', name)
console.log('解构后的age:', age)
const changeName = () => {
name.value += '哈' // name改变了,触发响应式更新
console.log(`name: ${name.value}, person.name: ${person.name}`);
}
const changeAge = () => {
age.value ++ // age改变了,触发响应式更新
console.log(`age: ${age.value}, person.age: ${person.age}`);
}
</script>
let { name, age } = toRefs(person);
使用toRefs()
将响应式对象person
解构为name
和age
两个响应式引用:
通过 toRefs()
将 person
对象解构后得到的 name
和 age
是响应式引用,它们与原始对象 person
的对应属性保持着响应式的关联。
对name
和age
的修改会触发响应式更新,并且会同步到原始的响应式对象person
中:
在模板中,可以使用响应式引用来访问和显示特定属性的值:
<template>
<div>
<p>姓名: {{ name }}</p>
<p>年龄: {{ age }}</p>
<button @click="changeName">修改名字</button>
<button @click="changeAge">修改年龄</button>
</div>
</template>
在模板中直接使用 name
和 age
与使用 person.name
和 person.age
的效果是一样的,都能正确地展示响应式数据,并且在数据发生变化时自动更新视图。
toRefs()
在调用时只会为源对象上可以枚举的属性创建 ref
。如果要为可能还不存在的属性创建 ref
,请改用 toRef()
。
toRef()
toRef()
创建一个特定属性的响应式引用
toRef()
接收一个响应式对象和一个属性名作为参数,并返回一个响应式引用,指向原始响应式对象中的特定属性。- 与
toRefs()
不同,它只创建一个特定属性的响应式引用。
示例:
<template>
<div>
<p>姓名: {{ name }}</p>
<p>年龄: {{ age }}</p>
<button @click="changeName">修改名字</button>
<button @click="changeAge">修改年龄</button>
</div>
</template>
<script setup lang="ts">
import { reactive, toRef } from 'vue';
const person = reactive({
name: '张三',
age: 36
});
// 创建了一个名为 name 的响应式引用,指向 person 对象中的 name 属性
let name = toRef(person, 'name')
console.log(name)
// 创建了一个名为 age 的响应式引用,指向 person 对象中的 age 属性
let age = toRef(person, 'age')
console.log(age)
const changeName = () => {
name.value += '哈' // name改变了,触发响应式更新
console.log(`name: ${name.value}, person.name: ${person.name}`);
}
const changeAge = () => {
age.value ++ // age改变了,触发响应式更新
console.log(`age: ${age.value}, person.age: ${person.age}`);
}
</script>
控制台打印结果:
toRef()
可以将值、refs 或 getters 规范化为 refs (3.3+)
toRef()
可以接受一个普通的值,并将其包装成一个响应式引用。- 也可以接受一个已有的
ref
对象,直接返回这个ref
对象。 - 还可以接受一个
getter
函数,将其返回值包装成一个响应式引用。 - 返回值:返回一个新的
ref
对象,该对象指向原始响应式对象中的特定属性或者普通值、getter
函数的返回值。
将普通值转换为响应式引用
<template>
<div>
<p>count: {{ count }}</p>
<p>countRef: {{ countRef }}</p>
<button @click="changeCount">点击修改count</button>
<button @click="changeCountRef">点击修改countRef</button>
</div>
</template>
<script setup lang="ts">
import { toRef } from 'vue';
// 定义一个普通变量count,初始值为 10
let count = 10
console.log('count: ', count)
// 通过toRef转换把普通变量count为响应式引用countRef
let countRef = toRef(count)
console.log('countRef: ', countRef)
const changeCount = () => {
count++ // count改变了,但不会触发响应式更新
console.log('changeCount()---count:', count)
}
const changeCountRef = () => {
// 修改响应式引用countRef.value的值,触发响应式更新
// 同时也会更新原始的普通变量count的值
// 会触发count、countRef的响应式更新
countRef.value++
console.log('changeCountRef()---countRef: ', countRef)
}
</script>
执行changeCount
,count
的值改变了,但页面显示仍然是初始值10
,不会触发响应式更新:
执行changeCountRef
,通过修改响应式引用countRef.value
的值,触发响应式更新。会触发count
、countRef
的响应式更新:
在这个例子中,toRef()
将普通的数字值10
转换为一个响应式引用countRef
。对countRef.value
的修改会影响到原始的值,并且在响应式系统中触发更新。
处理已有ref
对象
import { ref, toRef } from 'vue';
const originalRef = ref(20);
const newRef = toRef(originalRef);
如果将一个已有的ref
对象传递给toRef()
,它会直接返回这个ref
对象。
使用getter
函数
import { reactive, toRef } from 'vue';
const state = reactive({
count: 0,
});
const getCount = () => state.count;
const countRef = toRef(state, getCount);
使用getter
函数getCount
来获取响应式对象state
中的count
属性的值。toRef()
将这个getter
函数的返回值包装成一个响应式引用countRef
。当state.count
的值发生变化时,countRef.value
也会相应地更新。
toRefs()
和 toRef()
的区别
- 应用场景不同:
toRefs()
适用于需要将整个响应式对象解构为多个独立响应式引用的情况,通常在函数参数传递或在多个地方使用响应式对象的不同属性时很有用。toRef()
适用于只需要创建单个属性的响应式引用的情况,比如在特定场景下只关注一个属性的变化。
- 返回值不同:
toRefs()
返回一个包含多个响应式引用的对象(批量解构)。toRef()
返回一个单个的响应式引用(一个一个的操作)。