vue3 响应式 API:ref() 和 reactive()

news2024/11/16 23:43:18

在 Vue 3 中,响应式系统是其核心特性之一,它使得数据的变化能够自动触发视图的更新。

官方文档:
响应式 API:核心
要更好地了解响应式 API,推荐阅读官方指南中的章节:
响应式基础 (with the API preference set to Composition API)
深入响应式系统

reactive()

基本概念

  • 作用:用于创建一个响应式的对象。如果对这个对象的属性进行修改,会自动触发视图的更新。
  • 可接收的数据类型:只能定义对象类型的响应式数据。使用reactive()定义基本类型会报错,要用ref
  • 语法let 响应式对象= reactive(源对象)
  • 返回值:一个Proxy的实例对象,简称:响应式对象
  • 类型
function reactive<T extends object>(target: T): UnwrapNestedRefs<T>

使用reactive()创建对象类型的响应式数据:

<template>
  <div>
    <p>姓名: {{ person.name }}</p>
    <p>年龄: {{ person.age }}</p>
  </div>
</template>
<script setup lang="ts">
import { reactive } from 'vue';

const person = reactive({
  name: '张三',
  age: 36
})
console.log(person)

// 报错:类型“number”的参数不能赋给类型“object”的参数。
let count = reactive(0)

</script>

控制台打印:
在这里插入图片描述

直接修改响应式对象的属性值,会触发依赖这些属性的组件重新渲染:

// 直接修改,立即响应
person.name = '李四'
person.age = 24

// 使用方法,调用方法后再响应
const changePerson = () => {
  person.name = '李四'
  person.age = 24
}

reactive()深层响应性

  • 对象的嵌套属性也具有响应式
    • 响应式转换是“深层”的:它会影响到所有嵌套的属性。一个响应式对象也将深层地解包任何 ref 属性,同时保持响应性。
<template>
  <div>
    <p>b: {{ obj.a.b }}</p>
    <p>d: {{ obj.a.c.d }}</p>
    <button @click="changeB">修改b</button>
    <button @click="changeD">修改d</button>
  </div>
</template>
<script setup lang="ts">
import { reactive } from 'vue';

const obj = reactive({
  a: {
    b: 10,
    c: {
      d: 20
    }
  },
});
const changeB = () => {
  obj.a.b++
}
const changeD = () => {
  obj.a.c.d++
}
</script>

在这个例子中,修改嵌套对象的属性也会触发响应式更新。
不管数据嵌套的有多深,reactive()一定会把数据变成响应式的。

  • 数组的响应式
    • 对响应式数组进行添加、删除、修改等操作都会触发依赖这个数组的组件重新渲染。
<template>
  <div>
    <ul>
      <li v-for="item in personArr" :key="item.id">
        姓名:{{ item.name }} , 年龄:{{ item.age }} 岁
      </li>
    </ul>
    <button @click="addPerson">添加</button>
  </div>
</template>
<script setup lang="ts">
import { reactive } from 'vue';

const personArr = reactive([
  {id: 1, name: '张三', age: 17 },
  {id: 2, name: '李四', age: 18 },
  {id: 3, name: '王二', age: 16 },
]);

console.log(personArr); 
const addPerson = () => {
  personArr.push({ id: 4, name: '张麻子', age: 16 });
};

</script>

控制台打印:
在这里插入图片描述

  • 直接赋值整个响应式对象不会触发响应式更新
let person = reactive({
  name: '张三',
  age: 36,
});

// 这样不会触发响应式更新
const changePerson = () => {
  person = {
    name: 'john',
    age: 30
  }
}

// 使用Object.assign()等方法来更新属性
const changePerson = () => {
  Object.assign(person, {
    name: '李四',
    age: 24,
  });
}
  • 响应式对象的属性必须在创建时存在
const person = reactive({
  name: '王二',
});

// 报错:类型“{ name: string; }”上不存在属性“age”。
person.age = 30;  

ref()

基本概念

ref()接受一个内部值,返回一个响应式的、可更改的 ref 对象,此对象只有一个指向其内部值的属性 .value

  • 作用:定义响应式的变量。
  • 可接收的数据类型:基本类型、对象类型的响应式数据。若ref接收的是对象类型,内部其实也是调用了reactive函数。
  • 语法let xxx = ref(初始值)。
  • 返回值:一个RefImpl的实例对象,简称ref对象或refref对象的value属性是响应式的。
  • 类型
function ref<T>(value: T): Ref<UnwrapRef<T>>

interface Ref<T> {
  value: T
}

ref 对象是可更改的,也就是说可以为 .value 赋予新的值。它也是响应式的,即所有对 .value 的操作都将被追踪。

使用 ref() 创建基本类型的响应式数据

<template>
  <div>
    <p>count: {{ count  }}</p>
  </div>
</template>
<script setup lang="ts">
import { ref } from 'vue'

// 在组合式 API 中,推荐使用 ref() 函数来声明响应式状态
const count = ref(0)
consoe.log(count)    // count 是一个RefImpl的实例对象
console.log('count.value:', count.value) // count.value:0

count.value = 1
console.log('count.value:', count.value) // count.value:1
</script>

控制台打印:
在这里插入图片描述
注意:

  • JS中操作数据需要:xxx.value,但模板中不需要.value,直接使用{{xxx}}
  • 对于const count = ref(0)来说,count不是响应式的,count.value是响应式的。

ref()深层响应性

ref()包裹的是一个对象时,对这个对象的属性进行修改也会触发响应式更新。

<template>
  <div>
    <p>姓名: {{ person.name }}</p>
    <p>年龄: {{ person.age }}</p>
    <button @click="changePerson">修改信息</button>
  </div>
</template>
<script setup lang="ts">
import { ref } from 'vue';

const person = ref({
  name: '张三',
  age: 36
})
console.log(person)

const changePerson = () => {
  person.name = '李四'
  person.age = 24
}
</script>

使用ref()定义一个对象类型的响应式数据,页面是正常展示数据的:
在这里插入图片描述
数据详细信息解析:

  • RefImpl {... }:这表示这是一个由ref函数创建的响应式对象的内部实现结构展示。
  • __v_isShallow:表示是否是浅层响应式,这里为false,说明不是浅层响应式。
  • dep:这是一个依赖收集器,用于跟踪哪些部分的代码依赖于这个响应式对象。当响应式对象的值发生变化时,会通知依赖它的部分进行更新。这里显示为一个包含一个ReactiveEffectMap,说明有一个依赖项。
  • __v_isRef:为true,表明这是一个由ref创建的响应式引用。
  • _rawValue:存储了原始的值,这里是一个包含nameage属性的对象。
  • _valuevalue:都是代理对象,通过代理可以实现对原始对象的响应式追踪。
  • [[Handler]][[Target]]:是与代理对象相关的内部属性,[[Handler]]是处理程序,用于定义对目标对象的各种操作的拦截行为,[[Target]]是被代理的原始对象。

从控制台打印的数据结构可以看出:如果将一个对象赋值给 ref,那么这个对象将通过 reactive() 转为具有深层次响应式的对象。

表面上它返回来的是一个RefImpl的实例对象,但是在这个实例对象的_valuevalue属性里,是 reactive() 的返回值:一个Proxy的实例对象。

直接调用changePerson修改信息,person没有被修改。编译器会报错:

const changePerson = () => {
  // 报错:类型“Ref<{ name: string; age: number; }>”上不存在属性“name”。
  person.name = '李四'
  // 报错:类型“Ref<{ name: string; age: number; }>”上不存在属性“age”。
  person.age = 24
  console.log(person)
}

ref定义的数据,如果要修改,要用.value来更改。

const changePerson = () => {
  console.log('修改前打印person.value:', person.value)
  person.value.name = '李四'
  person.value.age = 24
  console.log(person)
}

在这里插入图片描述

可以使用Vue - Official插件自动添加.value

在这里插入图片描述

在这里插入图片描述

ref()reactive()的区别

  1. 从数据类型看
    • ref()用来定义:基本类型数据对象类型数据
    • reactive()用来定义:对象类型数据
  2. 返回值类型
    • ref()返回值: 一个RefImpl的实例对象。
    • reactive()返回值:一个Proxy的实例对象,简称:响应式对象。
  3. 响应式更新方式
  • ref()通过修改.value属性来触发响应式更新。
    ref()跳过 .value属性直接修改变量的值,不会出发响应式更新。
import { ref } from 'vue';
let person = ref({name: '张三', age: 36});
const changePerson = () => {
  // 重新分配一个对象,触发响应式更新
  person.value = { name: '李四', age: 24 }
  // ref 跳过 .value,不会出发响应式更新
  person = { name: '李四', age: 24 }
}

let count = ref(0)
const changeCount = () => {
  // 可以触发响应式更新
  count.value ++;

  // 不会触发响应式更新
  count = ref(10)
}
  • reactive()直接修改对象的属性即可触发响应式更新。
import { reactive } from 'vue';
let person = reactive({name: '张三', age: 36});
const changePerson = () => {
  person.name = '李四'   // 触发响应式更新
  person.age = 30        // 触发响应式更新
}

  • reactive()重新分配一个新对象,会失去响应式(可以使用Object.assign去整体替换)。
import { reactive } from 'vue';
let person = reactive({name: '张三', age: 36});

// 不会触发响应式更新
const changePerson = () => {
  // 重新分配一个对象
  person = { name: '李四', age: 24 }
  
  // person = reactive({name: '张三', age: 36});
  // 与person = reactive({ name: '李四',age: 24 });
  // 是完全不同的2个完全不同的person, 根本不是同一个东西
  person = reactive({ name: '李四', age: 24 });
}

// 使用Object.assign()等方法来更新属性
const changePerson = () => {
  Object.assign(person, {
    name: '李四',
    age: 24,
  });
}
  1. 深层响应性

    • reactive()可以自动保持对象的深层响应性,即嵌套对象的属性修改也会触发响应式更新。
    • 对于ref()包裹的对象,直接修改嵌套对象的属性可能不会触发响应式更新,需要特殊处理。
  2. 使用场景

    • 若需要一个基本类型的响应式数据,必须使用ref()
    • 若需要一个响应式对象,层级不深,ref()reactive()都可以。
    • 若需要一个响应式对象,且层级较深,推荐使用reactive()

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【STM32单片机_(HAL库)】3-2-1【中断EXTI】【电动车报警器项目】震动点灯

1.硬件 STM32单片机最小系统LED灯模块震动传感器模块 2.软件 exti驱动文件添加GPIO常用函数中断配置流程main.c程序 #include "sys.h" #include "delay.h" #include "led.h" #include "exti.h"int main(void) {HAL_Init(); …

Linux常用命令 ---- rmdir 命令[删除一个空目录]

rmdir 命令 功能&#xff1a;删除一个空目录 我们使用 mkdir 命令创建一个名为 test 空文件夹&#xff0c;如下图所示。 现在使用 rmdir 命令将 test 文件夹进行删除&#xff0c;如下图所示。 注意&#xff1a;rmdir 命令只能删除一个空目录&#xff0c;如果这个目录中有其他文…

【云原生】Kubernetes中的名称空间和资源配额详细用法与应用实战

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全…

医疗器械维修其实没有想的那么难

在很多人的印象中&#xff0c;医疗器械维修是一项极其复杂且神秘的工作&#xff0c;似乎只有专业的技术精英才能胜任。然而&#xff0c;事实并非如此&#xff0c;医疗器械维修其实并没有想象中那么难。 首先&#xff0c;现代医疗器械的设计越来越注重人性化和可维护性。制造商…

迎接开学新生活!高三开学必备物品推荐~

步入高三&#xff0c;意味着每一位学子都将面临人生中重要的转折点——高考。为了帮助高三学生们准备充分&#xff0c;让学习生活之路更加顺畅。今天小编综合了实用性、性价比以及学生需求的考量&#xff0c;精选了一系列必备物品&#xff0c;旨在为高三学生创造一个更为舒适、…

ICMP互联网控制报文协议

ICMP 互联网控制报文协议 ICMP &#xff08; Internet Control Message Protocol &#xff0c;也就是互联⽹控制报⽂协议&#xff09;。 ⽹络包在复杂的⽹络传输环境⾥&#xff0c;常常会遇到各种问题。 当遇到问题的时候&#xff0c;总不能死个不明不⽩&#xff0c;没头没脑…

4. kafka消息监控客户端工具

KafkaKing官网地址 : https://github.com/Bronya0/Kafka-King github下载地址 : Releases Bronya0/Kafka-King (github.com) (windows、macos、linux版本) 云盘下载地址 : https://pan.baidu.com/s/1dzxTPYBcNjCTSsLuHc1TZw?pwd276i (仅windows版本) 连接kafka 输入本地地址…

基于Java语言的私家车充电桩系统+私家车充电平台+充电桩系统项目

介绍 SpringBoot 框架&#xff0c;私家车充电桩平台充电桩系统充电平台充电桩云快充协议1.5-1.6协议新能源汽车二充电平台源码Java源码私家车充电系统 源码合作 提供无加密源代码和数据库&#xff0c;支持二次开发 SpringMVC架构完整充电桩系统源代码-充电桩系统-家充公充-新…

大数据报表如何免费设计?本攻略附赠强大报表工具!

在当今信息爆炸的时代&#xff0c;大数据跃升为企业战略决策的核心支撑点。如何有效地从浩瀚的数据海洋中精炼出富含洞察力的信息&#xff0c;并将其转化为直观易懂的报表&#xff0c;是每个数据分析师和决策者都需要面对的挑战。这需要掌握一定的技巧和经验&#xff0c;本文将…

C语言03--控制流

1.二路分支 逻辑&#xff1a;程序中某段代码需要在满足某个条件时才能运行形式&#xff1a; if 语句&#xff1a;表达一种 如果-则 的条件执行关系if-else 语句&#xff1a;表达一种 如果-否则 的互斥分支关系 语法&#xff1a; if ( 判断表达式 ) { // 代码块 } 解…

大学生最佳就业城市排行榜出炉!

最佳就业城市 随着秋招陆续开始&#xff0c;不少高校毕业生迎来了人生转折点。 其中一个需要重点考虑的点&#xff0c;是要前往哪座城市作为就业第一站。 不妨参考一下就业蓝皮书的统计数据&#xff1a; 可以发现&#xff0c;一线城市的就业人数正逐步减少&#xff0c;"新…

grid-template-columns: 1.833333rem 1fr;

问: grid-template-columns: 1.833333rem 1fr;这是什么属性? 回答: grid-template-columns: 1.833333rem 1fr; 定义了一个网格布局的列宽&#xff1a; 1.833333rem 表示第一列的宽度是相对于根元素字体大小的固定宽度, 1fr 是一个灵活单位&#xff0c;表示第二列会占据网…

【吊打面试官系列-Memcached面试题】memcached 最大的优势是什么?

大家好&#xff0c;我是锋哥。今天分享关于 【memcached 最大的优势是什么&#xff1f;】面试题&#xff0c;希望对大家有帮助&#xff1b; memcached 最大的优势是什么&#xff1f; Memcached 最大的好处就是它带来了极佳的水平可扩展性&#xff0c;特别是在一个巨大的系统中。…

[Python办公]Pandas创建透视表入门2

pivot_table 透视表在 Pandas 中是一个非常强大和灵活的工具&#xff0c;它支持许多高级功能&#xff0c;可以用于复杂的数据分析和报告生成。以下是一些更高级的用法和详细说明 1. 多级索引&#xff08;MultiIndex&#xff09; pivot_table 支持多级索引&#xff0c;这意味着…

iPhone16操作按钮大变样?引入快门拍摄按钮,提前告诉你它要干啥

随着智能手机摄影功能的不断升级&#xff0c;用户对于高质量照片和视频的需求也日益增长。苹果公司一直以来都在引领移动摄影技术的发展方向&#xff0c;而即将到来的iPhone 16系列更是备受瞩目。据多个消息来源透露&#xff0c;iPhone 16将引入一项创新功能——一个专门设计用…

【python实现修改所有可执行程序的图标】

实现效果&#xff1a; 图标在此 替换前&#xff1a; 吐槽&#xff1a;这原版看着也不像原版&#x1f603; 替换后&#xff1a; 代码&#xff1a; 注&#xff1a;必须要.ico图标文件 import winreg import ctypes import sys import os# 使用管理员身份打开程序 ctypes.windll…

3万多育儿宝典育儿网站ACCESS\EXCEL数据库

找了下以前弄到的一些育儿数据&#xff0c;发现小数据的《育儿宝典育儿知识大全ACCESS数据库》《结构漂亮的怀孕手册ACCESS数据库》、《结构漂亮的亲子宝典ACCESS数据库》&#xff0c;大的数据有《3万妈妈说育儿百科知识ACCESS数据库》而今天又弄到了一个3万多的育儿宝典网站&a…

ArcGIS Pro基础:状态栏显示栏的比例尺设置和经纬度位置

上图所示&#xff0c;界面下方最左侧是显示的比例尺&#xff0c;可以进行选择设置&#xff0c;也可以进行自定义设置 上图所示&#xff0c;可以手动录入比例尺&#xff0c;同时也可以对比例尺设置别名&#xff0c;比如【实验1】作为特定比例尺的标记 如上图所示&#xff0c;可以…

火语言RPA流程组件介绍--变量持久化及读取

变量持久化及读取 对于一些常用变量在下次或其他流程需要继续使用时&#xff0c;可以将内存中的变量持久化保存到该组件格式化的特定本地文件中&#xff0c;在下次或其他流程使用时从文件读取数据输出至下一个组件或变量&#xff0c;常用来保存难以初始化或者在流程中进行特定…

解决方案上新了丨趋动科技牵手谐云,联合打造支持算力池化的边缘计算解决方案

得益于近些年发展得如火如荼的云原生技术、尤其是作为云原生技术底座的容器云技术的高速发展和产业化落地&#xff0c;业界发现&#xff0c;将GPU等算力资源容器化、资源池化&#xff0c;可以将算力的管理能力拓展到整个数据中心。 作为国内为数不多掌握底层核心技术的容器云产…