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

前言

直接解构响应式对象的属性进行赋值给新的变量,会导致新变量失去响应式。
当修改新变量的值时,不会触发原始响应式对象的更新,从而在模板中也不会有相应的视图更新。

示例:

<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()函数来保持解构后的属性的响应式:

<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中:
在这里插入图片描述
在模板中,可以使用响应式引用来访问和显示特定属性的值:

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

示例:

<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>

执行changeCountcount的值改变了,但页面显示仍然是初始值10,不会触发响应式更新:
在这里插入图片描述
执行changeCountRef,通过修改响应式引用countRef.value的值,触发响应式更新。会触发countcountRef的响应式更新:
在这里插入图片描述
在这个例子中,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() 的区别

  1. 应用场景不同
    • toRefs()适用于需要将整个响应式对象解构为多个独立响应式引用的情况,通常在函数参数传递或在多个地方使用响应式对象的不同属性时很有用。
    • toRef()适用于只需要创建单个属性的响应式引用的情况,比如在特定场景下只关注一个属性的变化。
  2. 返回值不同
    • toRefs()返回一个包含多个响应式引用的对象(批量解构)。
    • toRef()返回一个单个的响应式引用(一个一个的操作)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/870627.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

案例分享—国外深色UI界面设计赏析

在国外&#xff0c;深色界面设计&#xff08;Dark Mode&#xff09;已成为提升用户体验的重要趋势。它不仅有效减少屏幕亮度&#xff0c;保护用户视力&#xff0c;还能在夜晚或低光环境下提供更加舒适的浏览体验。设计师们普遍认识到&#xff0c;深色主题不仅提升了应用的视觉层…

android13禁用打开wifi ap 热点

总纲 android13 rom 开发总纲说明 目录 1.前言 2.情况分析 3.代码分析 4.代码修改 5.彩蛋 1.前言 这个文章介绍的是如何禁止用户打开wifi热点,禁止用户安装app后,打开wifi热点。 2.情况分析 android13 应用层打开wifi AP public void setWifiApEnabled(boolean isEn…

qt-17不规则窗体

不规则窗体 知识点shape.hshape.cppmain.cpp运行图 知识点 感觉这个就是在图片背景 贴了白色 shape.h #ifndef SHAPE_H #define SHAPE_H#include <QWidget>class Shape : public QWidget {Q_OBJECTpublic:Shape(QWidget *parent nullptr);~Shape(); protected:void m…

HTML及CSS面试题4

1、BFC 1.1、介绍BFC及其应用 补充——触发BFC的方式&#xff0c;常见的有&#xff1a; 设置浮动overflow设置为&#xff1a;auto、scroll、hiddenpositon设置为&#xff1a;absolute、fixed 介绍&#xff1a; ○ 所谓BFC&#xff0c;指的是&#xff1a;一个独立的布局环境&am…

go-zero中间件的使用

一、自定义中间件 1、在api中在服务中定义一个中间件,名字随便取 type PostDemoReq {Name string json:"name" validate:"required" // 姓名Age int64 json:"age" validate:"required,gte1,lte130" // 年龄// optional 表示可选,omi…

漏洞挖掘 | 某系统webpack接口泄露引发的一系列漏洞

信息搜集 这里找到从小穿一条裤子长大的兄弟&#xff0c;要挟他交出来他的统一账号&#xff0c;否则把小时候的照片挂网上&#xff0c;开始某大学的资产搜集&#xff0c;直接hunter搜索此大学域名 看有价值的站点&#xff0c;ok找到下面的站点 未授权敏感信息泄露越权任意用…

docker compose部署rabbitmq集群,并使用haproxy负载均衡

一、创建rabbitmq的data目录 mkdir data mkdir data/rabbit1 mkdir data/rabbit2 mkdir data/rabbit3 二、创建.erlang.cookie文件&#xff08;集群cookie用&#xff09; echo "secretcookie" > .erlang.cookie 三、创建haproxy.cfg配置文件 global log stdout fo…

018集——递归函数和for循环对比(从1加到100实例) ——C#学习笔记

本例分别用递归函数和for循环&#xff0c;求出1到100的和&#xff1a; using System;namespace Mytest {class Mytest{static void Main(string[] args){Calculate Myc new Calculate();int YourNumber 100;int Mysum Myc.Xto1(YourNumber);Console.WriteLine("从1加到…

Go-Zero微服务框架下开发接口流程

目录 一&#xff1a;定义api入参和返回值 二&#xff1a;生成入参和返回值文件 三&#xff1a;定义rpc参数和返回值 四&#xff1a;生成返回值和参数 五&#xff1a;定义数据库 六&#xff1a;生成数据库文件 今天我们来讲解下如何在Go-Zero下开发一个api接口的具体流程&…

Android 13 GMS 内置壁纸

如图&#xff0c;原生系统上&#xff0c;设备上的壁纸 显示系统内置壁纸。如果没有添加内置壁纸&#xff0c;就显示默认的壁纸。点击进去就是预览页面 扩展下&#xff0c;默认壁纸在 frameworks/base/core/res/res/drawable-sw720dp-nodpi/default_wallpaper.png frameworks/b…

Markdown导出为 Excel文件 Vue3

直接复制到单文件内即可使用 需要用到的插件 xlsx 0.17.5marked 14.0.0file-saver 2.0.5vue 3.4.29 直接SFC单文件内使用 <script setup> import {reactive} from vue; import xlsx from xlsx; import {marked} from marked; import {saveAs} from file-saver;const…

博达网站群管理平台 v6.0使用相关问题解决

1 介绍 最近受人所托&#xff0c;需要用博达网站群管理平台创建一个网站。该平台的内部版本为9.8.2。作为一个能直接从代码创建网站系统的人&#xff0c;初次使用本平台&#xff0c;刚开始感觉摸不着头脑。因为该平台存在的目的&#xff0c;就是让不懂代码的人能快速创建网站&…

Go语言导入gin包

访问https://pkg.go.dev/页面,输入gin 点击README&#xff0c;点击Getting started&#xff0c;点击Getting Gin。 以VSCode通过mod命令导入gin包为例 安装第三方库 go mod init go mod tidy运行成功创建go.mod文件 go get -u github.com/gin-gonic/gin创建Go项目&#xf…

el-image 图片预览时 与 el-table (或avue-crud) 样式冲突 的解决

问题: 解决 <style scoped> ::v-deep(.el-table__cell) {position: static !important; } </style> 后效果

尚品汇-前端调用搜索实现(三十二)

目录&#xff1a; &#xff08;1&#xff09;修改web-all模块 &#xff08;2&#xff09;配置网关 &#xff08;3&#xff09;页面渲染 &#xff08;4&#xff09;面包屑处理 &#xff08;1&#xff09;修改web-all模块 修改pom.xml文件 <dependencies><depende…

手撕C++入门基础

1.C介绍 C课程包括&#xff1a;C语法、STL、高阶数据结构 C参考文档&#xff1a;Reference - C Reference C 参考手册 - cppreference.com cppreference.com C兼容之前学习的C语言 2.C的第一个程序 打印hello world #define _CRT_SECURE_NO_WARNINGS 1 // test.cpp // …

软件测试---接口测试

一、接口及接口测试概念 &#xff08;1&#xff09;接口的类型 &#xff08;2&#xff09;接口测试的概念 &#xff08;3&#xff09;接口测试的原理 &#xff08;4&#xff09;接口测试的特点 &#xff08;5&#xff09;接口测试的实现方式 二、HTTP协议 &#xff08;1&#…

C++:哈希

目录 unordered系列关联式容器 1&#xff1a;unordered_map And unordered_multimap 2&#xff1a;unordered_set And unordered_multiset 3&#xff1a;哈希冲突 4&#xff1a;哈希冲突 5&#xff1a;哈希函数 6&#xff1a;解决哈希冲突 1&#xff1a;闭散列 2&#xf…

Linux高编-进程的概念(1)

目录 1.ps aux 2.top 3.kill -2 进程pid // fork函数 getpid拿自己的进程号 getppid拿父进程号 fork&#xff08;&#xff09;&&fork&#xff08;&#xff09;||fork&#xff08;&#xff09; 父子进程的关系&#xff1a; 僵尸进程&#xff0c;孤儿进程 僵…

招生简章不会设计?这个网站可以供你参考

招生简章是学校与潜在学生之间的第一座桥梁&#xff0c;它的设计直接影响到学校的形象和招生效果。如果你在设计招生简章时感到困惑&#xff0c;不妨参考以下几个要点&#xff0c;让你的招生简章更加吸引人。 1.明确目标受众&#xff1a;在设计招生简章之前&#xff0c;首先要明…