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

news2024/12/23 8:07:46

前言

前面两章,给大家讲解了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 .

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

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

相关文章

VUE3 学习笔记(3):VUE模板理念、属性绑定、条件渲染、列表渲染

准备 1.清空不必要的项目文件 项目/src/assets/ 目录文件清空 项目/src/components/ 目录文件清空 删除main.js 的css引用 App.vue 代码如下 <template> </template> <script>//注意这里默认有一个setup 去掉 </script> 运行一下无错误提示就可以了…

【教学类-58-05】黑白三角拼图05(2-10宫格,每个宫格随机1张-6张,带空格纸,1页3张黑白3张白卡)

背景需求&#xff1a; 【教学类-58-04】黑白三角拼图04&#xff08;2-10宫格&#xff0c;每个宫格随机1张-6张&#xff0c;带空格纸&#xff0c;1页6张黑白&#xff0c;1张6张白卡&#xff09;-CSDN博客文章浏览阅读582次&#xff0c;点赞16次&#xff0c;收藏3次。【教学类-58…

无人机的相关基础知识(看不懂了 待定以后继续补充)

视频&#xff1a; 【浙江大学】浙大博导带你从0制作无人机_哔哩哔哩_bilibili 什么是无人飞行器 无人机自主导航构架 IMU&#xff08;加速度计和陀螺仪&#xff09;&#xff0c;可以测出当前的 加速度和角速度 这俩信息再去融合外部传感器 &#xff08;例如视觉传感器或者雷…

一款网站源码下载开源工具 Website Downloader

一、简介 Website Downloader 是一款免费的网站源码下载器&#xff0c;下载任何网站的完整源代码&#xff0c;包括 JavaScript、样式表、图像等等&#xff0c;而且使用也非常简单&#xff0c;只需要粘贴网址&#xff0c;用户即可将网页链接内容下载到本地&#xff0c;而且自动…

文件操作知识点

前言: 我们应该知道一般程序运行时产生的数据是存放在内存中的。但是如果程序关闭后这些内存就会被系统回收&#xff0c;如果内存内的有用的数据没有被保存下来&#xff0c;这些数据就丢失了。所以这个时候我们就可以使用磁盘来储存我们的数据。 目录 程序文件的分类 文件名…

Jenkins 流水线(Pipeline)详解

大家好&#xff0c;Jenkins 流水线&#xff08;Pipeline&#xff09;是一种可编排的持续集成和交付&#xff08;CI/CD&#xff09;方法&#xff0c;它以代码的方式定义整个软件开发过程中的构建、测试和部署流程。接下来就跟大家分享一下Jenkins 流水线&#xff08;Pipeline&am…

MySQL:图文超详细教程MySQL5.7下载与安装

一、前言 MySQL 5.7 是一个重要的数据库管理系统版本&#xff0c;它带来了多项改进和新特性&#xff0c;本文将超详细的带大家手动安装一下MySQL5.7。 二、下载MySQL5.7版本 MySQL5.7安装包 链接&#xff1a;https://pan.baidu.com/s/1lz5rp9PwfyeHzkEfI_lW6A 提取码&#…

基于小波分析和机器学习(SVM,KNN,NB,MLP)的癫痫脑电图检测(MATLAB环境)

癫痫是一种由大脑神经元突发性异常放电导致的大脑功能性障碍疾病。据世界卫生组织统计&#xff0c;全球约有7000万人患有癫痫。癫痫患者在发病时呈现肌肉抽搐、呼吸困难、意识丧失等症状。由于癫痫发作的偶然性&#xff0c;患者极有可能在高空、驾驶、游泳等危险情况下发病并丧…

基于混合Transformer-CNN模型的多分辨率学习方法的解剖学标志检测

文章目录 Anatomical Landmark Detection Using a Multiresolution Learning Approach with a Hybrid Transformer-CNN Model摘要方法实验结果 Anatomical Landmark Detection Using a Multiresolution Learning Approach with a Hybrid Transformer-CNN Model 摘要 精确定位…

数据分析实战:从0到1完成数据获取分析到可视化

文章目录 1.数据分析基本流程1.1 数据采集1.2 数据提炼1.3 数据探索分析 2.数据获取的方法和工具2.1 数据解锁器2.2 爬虫浏览器2.3 数据洞察市场 3.完整案例分析&#xff1a;从数据采集到数据可视化3.1 直接按需定制数据集获取数据3.2 获取IP代理&#xff0c;利用python爬取数据…

JavaScript Window对象

一、BOM&#xff08;浏览器对象模型&#xff09; window对象是一个全局对象&#xff0c;也可以说是JavaScript中的顶级对象。 像document、alert()、console.log()这些都是window的属性&#xff0c;基本BOM的属性和方法都是window的。 所有通过var定义在全局作用域中的变量、…

WindowsLinux环境下Spring Boot启动和关闭脚本

GitHub&#xff1a;SpringBootDemo Gitee&#xff1a;SpringBootDemo 微信公众号&#xff1a; 如果我们Spring Boot服务直接部署在Windows或Linux环境下&#xff0c;如果我们启动或关闭服务&#xff0c;需要频繁手敲命令&#xff0c;很不方便。 此时&#xff0c;我们可以编…

基于微信小程序的在电影线订票小程序+web管理 uniapp,vue,ssm

基于微信小程序的在电影线订票小程序web管理 uniapp&#xff0c;vue&#xff0c;ssm 相关技术 javassmuniapp微信开发者工具hbuildervueelementui前后端分离 -mysql

前端开发工程师——webpack

一.环境准备 npm init -y npm i webpack webpack-cli -D 打包命令 npx webpack ./src/main.js --modedevelopment //development开发模式 //production生产模式 npx webpack 直接运行就行 二.加载器loader 在less/stylus/css/sass/images中添加适当的样式 例如&#xff1…

【时间复杂度空间复杂度】(详)

&#x1f320;个人主页 : 赶路人- - &#x1f30c;个人格言 : 要努力成为梧桐&#xff0c;让喜鹊在这里栖息。 要努力成为大海&#xff0c;让百川在这里聚积。 13.with,prep.用&#xff0c;与&#xff0c;随着 [wɪθ] 14.format,n.格式 [ˈfɔrmt] 1.算法效率 算法效率分析分…

SparkStreaming概述

Spark概述 SparkStreaming概述 Spark Streaming 是 Apache Spark 生态系统中的一个组件&#xff0c;用于实时流数据处理。它允许用户通过流式计算引擎处理实时数据流&#xff0c;并以低延迟的方式对数据进行分析、处理和存储。 背景 在大数据领域&#xff0c;传统的批处理系统…

Go语言实现简单分布式系统(笔记)

视频&#xff1a; Go语言编写简单分布式系统&#xff08;完结&#xff09;_哔哩哔哩_bilibili&#xff0c;作者&#xff1a;杨旭&#xff0c;非常感谢&#xff0c;大佬真牛批 参考笔记及代码&#xff1a; Go语言实现简单分布式系统 - N3ptune - 博客园 (cnblogs.com) 整体框…

Nvidia 如何成为 AI 训练的超级强国

周三&#xff0c;英伟达公布了第一季度的财务业绩&#xff0c;再次超出了分析师的预期。在截至 4 月 28 日的季度中&#xff0c;该公司的利润同比飙升 262%&#xff0c;股价一度创下 1000 美元以上的新高。 目前&#xff0c;英伟达的市值超过 2.3 万亿美元&#xff0c;是全球第…

React自定义Componment和State深层次理解-07

本节主要从底层原理上分析下React开发相关的内容和注意事项&#xff0c;本节会围绕使用展开&#xff0c;而非源码讲解。 Componment详解 什么是组件 在 MVVM架构出现之前&#xff0c;组件主要分为两种。 狭义上的组件&#xff0c;又称为 UI 组件&#xff0c;比如 Tabs 组件、…

shell脚本开发基础

shell脚本开发基础 什么是linux内置命令&#xff1f;什么是外置命令 内置命令&#xff1a;在系统启动时就加载入内存&#xff0c;常驻内存&#xff0c;执行效率更高&#xff0c;但是占用资源&#xff0c;cd 外置命令&#xff1a;系统需要从硬盘中读取程序文件&#xff0c;再读…