第六节:带你全面理解vue3 浅层响应式API: shallowRef, shallowReactive, shallowReadonly

前言

前面两章,给大家讲解了vue3ref, reactive,readonly创建响应式数据的API, 以及常用的计算属性computed, 侦听器watch,watchEffect的使用

其中reactive, ref, readonly创建的响应式数据都是深层响应.

而本章主要给大家讲解以上三个API 对应的创建浅层响应式数据的 API, 即shallowRef, shallowReactive, shallowReadonly

1. shallowRef

shallowRefref 浅层作用形式。其实就是浅层响应式

shalloRef的使用方式和ref()完全相同, 不同的只是vue对数据响应式处理的不同:

  • ref创建的数据, 如果有深层, 比如参数是一个对象, 对象的属性就是深层, 参数对象会被vue通过reactive处理为深层响应
  • shallowRef创建数据, 只有顶层的value属性具有响应性, 深层的数据不具有响应性, 会原因返回, 即vue不会递归的将深层数据通过reactive处理, 而是原样存储和暴露.

1.1. 类型

我们先看一下shallowRefAPI 的类型签名, 其签名如下:

function shallowRef<T>(value: T): ShallowRef<T>

interface ShallowRef<T> {
  value: T
}

通过签名可以简单的看出, shallowRefAPI 接收一个参数, 返回一个具有value属性的对象, 且value属性的类型和参数类型相同.

1.2. ref 和shallowRef 处理原始数据类型

接下会通过示例带大家看一下refshallowRef 在处理原始数据类型的有什么不同.

示例代码:

<template>
  <div>
    <h3>shallowRef</h3>
    <div>{{ count }}</div>
    <div>{{ count2 }}</div>
    <button @click="change">修改数据源</button>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref, shallowRef } from 'vue'

export default defineComponent({
  setup() {
    const count = ref(0)
    const count2 = shallowRef(0)

    // 控制台输出 ref, shallowRef 创建的数据
    consollog("count", count);
    console.log("count2", count2);

    // 修改数据
    const change = () => {
      // count.value++
      count2.value++
    }
    return { count, count2, change }
  }
})
</script>

控制台输出结果

img

通过代码的运行结果, 你可以看出,

在参数为基本数据类型的情况下, refshallowRef创建的响应式数据在使用上完全一样, value属性都具有响应性

通过控制台输出结果, 你会发现refshallowRef创建的ref对象极度相似,
如果你阅读过源码, 你就会明白, ref, shallowRef返回对象是通过同一个类实例化的对象.因此两个实例对象具有相同的属性. 但是有一个属性__v_isShallow属性值不同, 因为vue通过这个属性来区分是ref还是shallowRef创建的对象.

这里不对源码实现做过多阐述, 如果你对源码感兴趣, 可以关注我的vue3源码专栏


1.3. ref 和shallowRef 处理深层对象

refshallowRef 在处理深层对象就会有所不同:

  • ref函数在处理深层对象时, 深层对象会被vue自动调用reactive包裹成响应式数据,
  • shallowRef函数在处理深层对象时, vue不同将深层对象包裹为响应式对象, 也就是说shallowRef只有.value属性值才具有响应性, 深层对象不具有响应性

示例:

<template>
  <div>
    <h3>shallowRef</h3>
    <div>{{ count }}</div>
    <div>{{ count2 }}</div>
    <button @click="change">修改数据源</button>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref, shallowRef } from 'vue'

export default defineComponent({
  setup() {
    const count = ref({ count: 0 })
    const count2 = shallowRef({ count: 0 })

    // 控制输出 ref, shallowRef 对于参数为对象时创建的ref 对象
    console.log('count', count)
    console.log('count2', count2)

    // 修改数据
    const change = () => {
      // count.value.count++

      // 不会触发响应式
      // count2.value.count++

      // count2 是shallowRef数据
      // shallowRef 数据只有通过.value 整体修改时才会触发响应式
      count2.value = { count: 3 }
    }
    return { count, count2, change }
  }
})
</script>

控制台输出:

img

通过控制台输出ref, shallowRef 创建的响应数据, 以及示例的运行结果, 会发现:

  • shallowRef在参数为深层对象时, 创建的ref数据, value值就是参数原对象, 不具有响应性
  • refvalue属性值, 是vue调用reactive函数包裹成的Proxy代理对象, 即响应式数据

因此, 你可以理解shallowRefref 浅层响应式的API, 只有通过.value修改数据才会触发响应式, 深层对象没有通过reactive包裹, 因此深层操作数据不具有响应式

shallowRef() 常常用于对大型数据结构的性能优化或是与外部的状态管理系统集成。


2. shallowReactive

shallowRefref关系相似,

shallowReactivereactive浅层作用形式。就是创建具有浅层响应性的数据


2.1. shallowReactive 类型

首先,我们先看一下shallowReactive类型签名, 签名如下:

function shallowReactive<T extends object>(target: T): T

通过签名可以看出, shallowReactive函数接收一个对象作为参数, 返回类型与参数类型相同

2.2. shallowReactive 浅层响应式

reactive() 不同,shallowReactive创建响应对象没有深层级的转换:一个浅层响应式对象里只有根级别的属性是响应式的, 深层对象不会被vue自动包装为响应对象.

示例:

<template>
  <div>
    <h3>shallowReactive</h3>
    <div>{{ user }}</div>
    <div>{{ user2 }}</div>
    <button @click="change">修改数据源</button>
  </div>
</template>

<script lang="ts">
import { defineComponent, reactive, shallowReactive } from 'vue'

export default defineComponent({
  setup() {
    const user = reactive({ name: '张三', age: 18, friend: { name: '李四' } })
    const user2 = shallowReactive({ name: '张三', age: 18, friend: { name: '李四' } })

    // 控制台输出reactive, shallowReactive 创建的深层对象
    console.log("reactive friend", user.friend);
    console.log("shallowReactive friend", user2.friend);

    
    // 修改数据
    const change = () => {
      // 1. reactive, shallowReactive 在处理第一层对象属性时
      // 都会触发响应式
      // user.name = "王五"  // 修改 reactive
      // user2.name = "王五"  // 修改 shallowReactive

      // 2. 操作深度属性,shallowReactive 不会触发响应性
      // user.friend.name = '王五'  // 修改 reactive
      user2.friend.name = '王五'  // 修改 shallowReactive  不触发响应式

    }
    return { user, user2, change }
  }
})
</script>

控制台输出结果:

img

通过控制台输出结果, 你应该已经看出:

  • reactive创建的响应数据(代理对象) 深层对象也会自动的调用reactive函数, 创建为响应数据
  • shallowReactive创建浅层响应数据, 其深层对象就是原样的不同对象, 不具有响应性.

运行结果也可以看出, 修改shallowReactive深层对象的数据, 页面是不会有任何变化的.因为不具有响应性.

2.3. shallowReactive 深层ref 数据不会自动解包

shallowReactive()函数创建一个浅层响应式对象里只有根级别的属性是响应式的。

也可以说shallowReactive()函数创建的数据是非深度监听, 只会包装第一个对象, 这也就意味着深层的ref数据不会被自动解包.

因为shallowReactive 深层数据的存储是原样存储, 不会包裹为深度响应式,

示例:

<template>
  <div>
    <h3>shallowReactive</h3>
    <div>{{ user }}</div>
    <div>{{ user2 }}</div>
    <button @click="change">修改数据源</button>
  </div>
</template>

<script lang="ts">
import { defineComponent, reactive, ref, shallowReactive } from 'vue'

export default defineComponent({
  setup() {
    const count = ref(10)
    const user = reactive({ name: '张三', age: 18, count })
    const count2 = ref(20)
    const user2 = shallowReactive({ name: '张三', age: 18, count: count2 })

    // 修改数据
    const change = () => {
      // 1. reactive 操作深层ref 数据时,自动解包
      // 此时修改user.count 数据时不用添加.value
      // user.count = 20  // 修改 reactive

      // 2. shallowReactive 操作深层ref 数据时,不会自动解包
      // 此时修改user2.count 数据时必须使用.value
      user2.count.value = 40  // 修改 shallowReactive


    }
    return { user, user2, change }
  }
})
</script>

2.4. shallowReactive与shallowRef 使用比较

  1. 一般情况下使用refreactive即可
  2. 如果有一个对象数据, 结构比较深, 但只有一层对象的属性变化会触发响应, 使用shallowReactive
  3. 如果有一个对象数据, 后面会产生新的对象来整体替换触发响应式, 使用shallowRef

3. shallowReadonly

shallowReadonlyreadonly浅层作用形式。只有第一层是只读的,深层不是只读属性,

也可以这么理解, 只有第一层的对象属性不能修改值, 但深层的数据是可以修改的, 是非深层只读

3.1. shallowReadonly 浅层只读

shallowReadonly在使用上与readonly完全相同, 区域在于:

  • readonly() 创建只读代理, 如果有深层对象, 深层对象也会自动调用readonly处理为只读代理
  • shallowReadonly 创建的是浅层只读代理, 也就是深层对象不会自动调用readonly包裹, 所以深层对象是非只读的, 即可以修改的. 也就意味着只有根层级的属性变为了只读。

示例:

<template>
  <div>
    <h3>shallowReadonly</h3>
    <div>{{ user }}</div>
    <div>{{ user2 }}</div>
    <button @click="change">修改数据源</button>
  </div>
</template>

<script lang="ts">
import { defineComponent, readonly, ref, shallowReadonly } from 'vue'

export default defineComponent({
  setup() {
    const user = readonly({ name: '张三', age: 18, friend: { name: '李四' } })
    const user2 = shallowReadonly({ name: '张三', age: 18, friend: { name: '李四' } })

    // 修改数据
    const change = () => {
      // readonly 为深层只读, 修改任何一层属性值都是不合法
      // user.name = '王五'
      // user.friend.name = '王五'


      // shallowReadonly 只有第一层会被转为只读, 深层属性会原样存储,不是只读的
      // user2.name = '王五'  // shallowReadonly 第一层修改报错,因为是只读的
     
      // shallowReadonly 修改生成不会报错,
      // 但也不会触发响应性, 因为原样存储就是一个普通对象
      user2.friend.name = '王五'


    }
    return { user, user2, change }
  }
})
</script>

3.2. shallowReadonly 处理深层ref不会自动解包

因为shallowReadonly 深层数据的存储是原样存储, 不会自动调用shallowReadonly转为只读, 因此对于深层的ref 的数据不会被自动解包了。

示例:

<template>
  <div>
    <h3>shallowReadonly</h3>
    <div>{{ user }}</div>
    <div>{{ user2 }}</div>
  </div>
</template>

<script lang="ts">
import { defineComponent, readonly, ref, shallowReadonly } from 'vue'

export default defineComponent({
  setup() {
    const count = ref(10)
    const user = readonly({ name: '张三', age: 18, count })
    const count2 = ref(20)
    const user2 = shallowReadonly({ name: '张三', age: 18, count: count2 })

    // readonly 处理深层数据为ref 数据时, 会自动解包,不用添加.value
    console.log('user.count', user.count)

    // shallowReadonly 获取深层ref 数据时必须添加.value, 因为不会自动解包
    console.log('user2.count', user2.count.value)

   
    return { user, user2 }
  }
})
</script>

4. 结语

至此, 就把shallowRef, shallowReactive,shallowReadonly给大家讲解完了, 这三个API使用上还是相对较少. 大家要理解这些API的使用原理, 工作中根据情况选择不同的API .

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

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

相关文章

使用Java和XxlCrawler获取各城市月度天气情况实践

目录 前言 一、历史数据获取 1、关于天气后报 2、信息界面分析 二、数据的提取开发 1、PageVo的定义 2、属性定义 3、实际信息抓取 三、信息抓取调试以及可能的问题 1、信息获取成果 2、关于超时的问题 四、总结 前言 这篇文章主要来源于一个我们家小朋友的一个作业…

数据库管理-第194期 网络加速RDMA初探(20240526)

数据库管理194期 2024-05-26 数据库管理-第194期 网络加速RDMA初探&#xff08;20240526&#xff09;1 概念2 发展3 使用总结 数据库管理-第194期 网络加速RDMA初探&#xff08;20240526&#xff09; 作者&#xff1a;胖头鱼的鱼缸&#xff08;尹海文&#xff09; Oracle ACE A…

GCANet去雾算法

目录 1. 引言 2. 门控上下文注意机制&#xff08;GCA&#xff09; 3. 去雾流程 4. 模型代码 5. GCANet的优势 6. 去雾效果 1. 引言 GCANet(Gate-Controlled Attention Network)是一种用于图像去雾的深度学习算法&#xff0c;通过引入注意力机制来改进传统的去雾方法&…

使OpenCV可以读取中文路径图片的方法

一&#xff0e;问题复现 1.代码 #! /usr/bin/env python # -*- coding: utf-8 -*-# File: show_img.pyimport cv2# 读取图片 img cv2.imread("车牌素材/冀A.png")# 显示图片 cv2.imshow("img", img) cv2.waitKey(0)2.报错截图 3.报错内容 [ WARN:00.05…

简单好用的文本识别方法--付费的好用,免费的更有性价比

文章目录 先说付费的进入真题&#xff0c;免费的来喏&#xff01;PixPin微信 先说付费的 直达网址!!! 进入真题&#xff0c;免费的来喏&#xff01; PixPin 商店里就有 使用示例&#xff1a; 可以看到&#xff1a;贴在桌面上的图片可以复制图片中的文字&#xff0c;真的很…

第一篇【传奇开心果系列】Python的跨平台开发工具beeware技术点案例示例:使用beeware实现跨平台开发,从hello world开始

传奇开心果博文系列 系列博文目录Python的跨平台开发工具beeware技术点案例示例系列 博文目录前言一、BeeWare套件主要功能介绍二、Toga相对于其他Python UI库具有的优势介绍三、使用toga开发安卓手机应用hello world步骤和示例代码四、使用toga写一个iOS 苹果手机应用hello wo…

红蓝对抗-HW红蓝队基本知识(网络安全学习路线笔记)

第一, 什么是蓝队 蓝队&#xff0c;一般是指网络实战攻防演习中的攻击一方。 蓝队一般会采用针对目标单位的从业人员&#xff0c;以及目标系统所在网络内的软件、硬件设备同时执行多角度、全方位、对抗性的混合式模拟攻击手段&#xff1b;通过技术手段实现系统提权、控制业务、…

高速数据采集与传输(一):ADC08D500调研

前言&#xff1a;高速ADC数据采集的应用和开发&#xff0c;涉及的技术面非常的广泛&#xff0c;后续阶段博主将尝试以纯项目开发的形式做一次专题技术分享&#xff0c;将基于高速数据采集的相关内容进行一系列的技术文档更新。博主全凭兴趣在更新和总结&#xff0c;很难做到一直…

unity开发Hololens 制作滑动框

一定要做到最后一步&#xff0c;才会有效果 1、创建空物体 ,并添加组件 创建空物体 命名ScrollingObjectCollection&#xff0c; 添加组件如下图 下面是各个组件展开的内容 2、在ScrollingObjectCollection 下面创建两个空物体&#xff0c;分别命名Container、Clipping…

[AI Google] 介绍 VideoFX,以及 ImageFX 和 MusicFX 的新功能

VideoFX 是来自 labs.google 的最新实验&#xff0c;您可以查看音乐效果和图像效果的新更新&#xff0c;现在在 110 多个国家可用。 生成式媒体正在改变人们构思创意并增强我们的创造力能力的方式。我们致力于与创作者和艺术家合作构建人工智能&#xff0c;以更好地理解这些生成…

C++第三方库 【HTTP/HTTPS】— httplib库

目录 认识httplib库 安装httplib库 httplib的使用 httplib请求类 httplib响应类 Server类 Client类 httplib库搭建简单服务器&客户端 认识httplib库 httplib库&#xff0c;是一个C11单头文件的&#xff0c;轻量级的跨平台HTTP/HTTPS库&#xff0c;可以用来创建简单的…

软件设计师干货资料分享

从2月份备考&#xff0c;到5月份结束&#xff0c;满打满算四个月准备时间。在此我想提醒一句&#xff0c;世界上没有什么不劳而获的事情&#xff0c;never&#xff0c;只要你是一个普通人&#xff0c;但凡你想索取一些什么&#xff0c;无一例外你都需要付出&#xff0c;而且是踏…

【每日力扣】84. 柱状图中最大的矩形 与 295. 数据流的中位数

&#x1f525; 个人主页: 黑洞晓威 &#x1f600;你不必等到非常厉害&#xff0c;才敢开始&#xff0c;你需要开始&#xff0c;才会变的非常厉害 84. 柱状图中最大的矩形 给定 n 个非负整数&#xff0c;用来表示柱状图中各个柱子的高度。每个柱子彼此相邻&#xff0c;且宽度为…

Linux操作指令大全

目录 &#x1f349;引言 &#x1f349; 基础命令 &#x1f348;pwd &#x1f348;cd &#x1f348;ls &#x1f348;mkdir &#x1f348;rmdir &#x1f348;cp &#x1f348;mv &#x1f348;rm &#x1f349; 文件操作命令 &#x1f348;cat &#x1f348;tac …

CentOS 7.9安装NVIDIA P40显卡驱动、CUDA和cuDNN

文章目录 1、安装P40显卡驱动1.1 查看机器上有哪些显卡1.2 禁用nouveau1.3 安装依赖1.4 安装驱动 2、安装CUDA2.1 安装2.2 测试是否安装成功 3、安装cuDNN3.1 安装3.2 测试是否安装成功 4、总结 1、安装P40显卡驱动 1.1 查看机器上有哪些显卡 lspci | grep -i vga lspci | gr…

《欢乐钓鱼大师》辅助:新手钓鱼全新攻略大全!

《欢乐钓鱼大师》是一款充满趣味和挑战的钓鱼游戏。在游戏中&#xff0c;玩家不仅可以体验钓鱼的乐趣&#xff0c;还可以通过不同的钓鱼竿和鱼卡来提升自己的钓鱼技能。为了帮助新手和老玩家更好地体验游戏&#xff0c;本文将为您提供详细的游戏攻略。 1. 游戏目标 在《欢乐钓…

2024年蓝桥杯Web开发【大赛大纲】15届

一、 组别 Web应用开发分为&#xff1a;大学组和职业院校组。 每位选手只能申请参加其中一个组别的竞赛。各个组别单独评奖。 研究生和本科生只能报大学组。 其它高职高专院校可自行选择报任意组别。 二. 竞赛赛程 省赛时长&#xff1a;4小时。 决赛时长&#xff1a;4小…

c语言——宏offsetof

1.介绍 &#xff01;&#xff01;&#xff01; offsetof 是一个宏 2.使用举例 结构体章节的计算结构体占多少字节需要先掌握&#xff08;本人博客结构体篇章中已经讲解过&#xff09; 计算结构体中某变量相对于首地址的偏移&#xff0c;并给出说明 首先&#xff0c;结构体首个…

SpringBoot项目中redis序列化和反序列化LocalDateTime失败

实体类中包含了LocalDateTime 类型的属性&#xff0c;把实体类数据存入Redis后变成这样&#xff1a; 此时&#xff0c;存入redis不会报错&#xff0c;但是从redis获取的时候&#xff0c;会报错&#xff1a; com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Ca…

[7] CUDA之常量内存与纹理内存

CUDA之常量内存与纹理内存 1. 常量内存 NVIDIA GPU卡从逻辑上对用户提供了 64KB 的常量内存空间&#xff0c;可以用来存储内核执行期间所需要的恒定数据常量内存对一些特定情况下的小数据量的访问具有相比全局内存的额外优势&#xff0c;使用常量内存也一定程序上减少了对全局…