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,使用方法changeName、changeAge修改name、age,本质上修改的是使用let声明的name变量、age变量,person的name属性、age属性不会改变。因此不会触发响应式更新。

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

在 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解构为name和age两个响应式引用:

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

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

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

html 复制代码
<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()不同,它只创建一个特定属性的响应式引用。

示例:

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>

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

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

在这个例子中,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()返回一个单个的响应式引用(一个一个的操作)。
相关推荐
子兮曰5 天前
jev-ultrafast 深度解析:7 秒订机票的浏览器 Agent 是如何炼成的
前端·后端·agent
子兮曰5 天前
Jev 爆发一周:7 秒 Agent 背后的 System One 生态与三场争议
前端·后端·ai编程
前端小万5 天前
写公众号赚了 3000 块后,我做了一款叫 "一键成稿" 的软件
前端·微信小程序
爱勇宝5 天前
ZCode 开源 24 小时:一份没有历史的账本,回答不了"有没有偷代码"
前端·后端·chatglm (智谱)
A黄俊辉A5 天前
uniapp webview中实现 app和内嵌的H5双向通信
vue.js·json
三十而立洋5 天前
Cookie 详解:从产生到安全,一次讲透
前端·javascript
卡布鲁5 天前
把一个 Vite + Vue3 应用塞进 qiankun (React + Umi3) 主站:十个坑的复盘
前端·javascript·react.js
李少兄5 天前
JavaScript 隐式全局变量解析
javascript
honkun65 天前
vue 表格组件 vxe-table 配置 ajax 请求自动加载数据与表单查询
vue.js·vxe-table
kybs19915 天前
全球灾害数据分析可视化 毕业设计-附源码66794
vue.js·spring boot·mysql·安全·django·c#·asp.net