vue3响应式工具 toRefs() 和 toRef()

前言

直接解构响应式对象的属性进行赋值给新的变量,会导致新变量失去响应式。

当修改新变量的值时,不会触发原始响应式对象的更新,从而在模板中也不会有相应的视图更新。

示例:

html 复制代码
<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,使用方法changeNamechangeAge修改nameage,本质上修改的是使用let声明的name变量、age变量,personname属性、age属性不会改变。因此不会触发响应式更新。

从响应式的数据person直接解构出来的nameage不是响应式的。

在 Vue 3 中,toRefs()toRef()都是用于处理响应式对象的工具函数,它们的作用是将响应式对象中的属性转换为独立的响应式引用。主要用来取出响应式对象里的属性,或者解构响应式对象。

toRefs()

toRefs()将一个响应式对象转换为一个普通对象,这个普通对象的每个属性都是指向源对象相应属性的 ref。每个单独的 ref 都是使用 toRef() 创建的。

使用toRefs()函数来保持解构后的属性的响应式:

html 复制代码
<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解构为nameage两个响应式引用:

通过 toRefs()person 对象解构后得到的 nameage 是响应式引用,它们与原始对象 person 的对应属性保持着响应式的关联。

nameage的修改会触发响应式更新,并且会同步到原始的响应式对象person中:

在模板中,可以使用响应式引用来访问和显示特定属性的值:

html 复制代码
<template>
  <div>
    <p>姓名: {{ name }}</p>
    <p>年龄: {{ age }}</p>
    <button @click="changeName">修改名字</button>
    <button @click="changeAge">修改年龄</button>
  </div>
</template>

在模板中直接使用 nameage 与使用 person.nameperson.age 的效果是一样的,都能正确地展示响应式数据,并且在数据发生变化时自动更新视图。

toRefs() 在调用时只会为源对象上可以枚举的属性创建 ref。如果要为可能还不存在的属性创建 ref,请改用 toRef()

toRef()

toRef()创建一个特定属性的响应式引用

  • toRef()接收一个响应式对象和一个属性名作为参数,并返回一个响应式引用,指向原始响应式对象中的特定属性。
  • toRefs()不同,它只创建一个特定属性的响应式引用。

示例:

html 复制代码
<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函数的返回值。

将普通值转换为响应式引用

html 复制代码
<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>

执行changeCountcount的值改变了,但页面显示仍然是初始值10,不会触发响应式更新:

执行changeCountRef,通过修改响应式引用countRef.value的值,触发响应式更新。会触发countcountRef的响应式更新:

在这个例子中,toRef()将普通的数字值10转换为一个响应式引用countRef。对countRef.value的修改会影响到原始的值,并且在响应式系统中触发更新。

处理已有ref对象

javascript 复制代码
import { ref, toRef } from 'vue';

const originalRef = ref(20);
const newRef = toRef(originalRef);

如果将一个已有的ref对象传递给toRef(),它会直接返回这个ref对象。

使用getter函数

javascript 复制代码
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() 的区别

  1. 应用场景不同
    • toRefs()适用于需要将整个响应式对象解构为多个独立响应式引用的情况,通常在函数参数传递或在多个地方使用响应式对象的不同属性时很有用。
    • toRef()适用于只需要创建单个属性的响应式引用的情况,比如在特定场景下只关注一个属性的变化。
  2. 返回值不同
    • toRefs()返回一个包含多个响应式引用的对象(批量解构)。
    • toRef()返回一个单个的响应式引用(一个一个的操作)。
相关推荐
欲游山河十万里1 分钟前
(02)ES6教程——Map、Set、Reflect、Proxy、字符串、数值、对象、数组、函数
前端·ecmascript·es6
明辉光焱1 分钟前
【ES6】ES6中,如何实现桥接模式?
前端·javascript·es6·桥接模式
PyAIGCMaster20 分钟前
python环境中,敏感数据的存储与读取问题解决方案
服务器·前端·python
baozhengw22 分钟前
UniAPP快速入门教程(一)
前端·uni-app
nameofworld31 分钟前
前端面试笔试(二)
前端·javascript·面试·学习方法·数组去重
帅比九日1 小时前
【HarmonyOS NEXT】实战——登录页面
前端·学习·华为·harmonyos
摇光931 小时前
promise
前端·面试·promise
hummhumm1 小时前
第 12 章 - Go语言 方法
java·开发语言·javascript·后端·python·sql·golang
hummhumm1 小时前
第 8 章 - Go语言 数组与切片
java·开发语言·javascript·python·sql·golang·database
麻花20131 小时前
WPF学习之路,控件的只读、是否可以、是否可见属性控制
服务器·前端·学习