Vue3源码reactive和readonly对象嵌套转换,及实现shallowReadonly

前言

官方文档中对reactive的描述:

响应式转换是“深层”的:它会影响到所有嵌套的属性。一个响应式对象也将深层地解包任何 ref 属性,同时保持响应性。

官方文档中对readonly的描述:

只读代理是深层的:对任何嵌套属性的访问都将是只读的。它的 ref 解包行为与 reactive() 相同,但解包得到的值是只读的。

这意味着嵌套对象内的对象拥有和原对象一样的功能。

简单的来实践测试一下:

<script setup>
import { isReactive, isReadonly, reactive, readonly } from "vue";const original = reactive({nested: {foo: 1,},array: [{ bar: 2 }],
});const copy = readonly(original);
</script><template>{{ isReactive(original.nested) }}{{ isReactive(original.array) }}{{ isReactive(original.array[0]) }}{{ isReadonly(copy.nested) }}
</template>

页面中显示四个true

那测试用例可以完善一下,测试之前实现的代码是否支持这样的功能。

reactive

reactive.spec.ts中添加测试用例:

it("nested reactive", () => {const original = reactive({nested: { foo: 1 },array: [{ bar: 2 }],});expect(isReacive(original.nested)).toBe(true);expect(isReacive(original.array)).toBe(true);expect(isReacive(original.array[0])).toBe(true);
});

执行单测,如期不通过。

实现

实现思路,访问嵌套对象内对象时候,将内部对象也实现响应式,即调用reactive方法。那前提是访问的是对象,则需要添加一个是否是对象的判断。

import { isObject } from "../shared";function createGetter(isReadonly = false) {return function get(target, key) {let res = Reflect.get(target, key);if (key === ReactiveFlags.IS_REACTIVE) {return !isReadonly;} else if (key === ReactiveFlags.IS_READONLY) {return isReadonly;}if (isObject(res)) {return reactive(res);}if (!isReadonly) {track(target, key);}return res;};
}

在公共方法中导出isObject方法,

export function isObject(val) {return val !== null && typeof val === "object";
}

执行单测yarn test reactive

readonly

readonly.spec.ts中完善对嵌套对象的断言,

it("happy path", () => {const original = { foo: 1, bar: { baz: 2 } };const wapper = readonly(original);expect(wapper).not.toBe(original);expect(wapper.foo).toBe(1);expect(isReadonly(wapper)).toBe(true);expect(isReadonly(original)).toBe(false);// 判断嵌套对象expect(isReadonly(wapper.bar)).toBe(true);
});

执行单测yarn test readonly,预期不通过。

实现

readonly的实现和reactive一致。

if (isObject(res)) {return isReadonly ? readonly(res) : reactive(res);
}

执行单测,

最后再执行全部测试用例,验证是否对其他功能存在影响。

shallowReadonly

如果你在上文中点进了官方文档的链接,在readonly的详细描述中:

要避免深层级的转换行为,请使用 shallowReadonly() 作替代。

readonly()不同,shallowReadonly没有深层级的转换:只有根层级的属性变为了只读。属性的值都会被原样存储和暴露,这也意味着值为ref的属性不会被自动解包了。

单测

新增shallowReadonly.spec.ts

shallowReadonly是浅层转换,还是用isReadonly进行检测,最外层的是可读的,深层次的不是。

it("should not make non-reactive properties reactive", () => {const original = reactive({ foo: 1, bar: { baz: 2 } });const wapper = shallowReadonly(original);expect(isReadonly(wapper)).toBe(true);expect(isReadonly(wapper.bar)).toBe(false);
});

实现

reactive.ts导出shallowReadonly方法,和readonly类似,只是handler参数不同,

export function shallowReadonly(raw) {return createActiveObject(raw, shallowReadonlyHandler);
}

baseHandler.ts导出shallowReadonlyHandler对象,

export const shallowReadonlyHandler = {}

handlerget操作都是根据createGetter方法生成,那shallowReadnoly的特点是最外层的是可读的,深层次的不是,可以像readonly一样添加一个参数来判断,如果是shallow直接返回结果,也不需要进行深层次的转换和track

function createGetter(isReadonly = false, shallow = false) {return function get(target, key) {let res = Reflect.get(target, key);if (key === ReactiveFlags.IS_REACTIVE) {return !isReadonly;} else if (key === ReactiveFlags.IS_READONLY) {return isReadonly;}if (shallow) {return res;}if (isObject(res)) {return isReadonly ? readonly(res) : reactive(res);}if (!isReadonly) {track(target, key);}return res;};
}

全局定义:

const shallowReadonlyGet = createGetter(true, true);

shallowReadonlyHandler对象和readonlyHandler相似,只是get不同,可以复用之前实现的extend方法。

export const shallowReadonlyHandler = extend({}, readonlyHandler, {get: shallowReadonlyGet,
});

那可以顺便验证shallowReadonly的更新操作的测试用例,

it("should call console warn when call set", () => {console.warn = jest.fn();const original = shallowReadonly({ foo: 1 });original.foo = 2;expect(console.warn).toHaveBeenCalled();
});

执行单测yarn test shallowReadonly

总结

  1. reactive响应式转换是“深层”的:它会影响到所有嵌套的属性。readonly只读代理是深层的,对任何嵌套属性的访问都将是只读的。
  2. 获取对象深层次嵌套,会触发get操作,在判断该嵌套目标也是对象类型时就可以再次用reactivereadonly包裹返回。
  3. shallowReadonly就是在深层次转换和track逻辑之前返回结果即可。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

web:[BUUCTF 2018]Online Tool

题目 打开页面显示如下&#xff0c;进行代码审计 上述代码主要功能是接收‘host’参数&#xff0c;后使用nmap扫描主机端口 首先检查是否存在HTTP_X_FORWARDED_FOR头&#xff0c;若存在&#xff0c;将值赋值给EMOTE_ADDR,是为了跟踪用户真实的IP地址 后用检查get‘host’是否…

验证k8s中HPA功能及测试

部署 使用yaml部署服务 apiVersion: apps/v1 kind: Deployment metadata:name: php-apachenamespace: tools spec:replicas: 1selector:matchLabels:app: php-apachetemplate:metadata:labels:app: php-apachespec:containers:- name: php-apacheimage: registry.cn-beijing.…

Ubuntu18.04平台下Qt开发程序打包的一些问题总结

目录 前言 一、在Ubuntu18.04开发环境下打包有两种方式 1、利用linuxdeployqt软件进行打包 2、利用编写shell脚本的方式进行打包 二、详细介绍shell脚本打包的方式 1、新建一个空的文件夹 2、准备脚本copylib.sh 3、准备脚本xxxx.sh。 4、给上述两个脚本添加可执行权限…

Java虚拟机运行时数据区结构详解

Java虚拟机运行时数据区结构如图所示 程序计数器 程序计数器&#xff08;Program Counter Register&#xff09;是一块较小的内存空间&#xff0c;它可以看作是当前线程所执行的字节码的行号指示器。 多线程切换时&#xff0c;为了能恢复到正确的执行位置&#xff0c;每条线程…

计算机毕业设计选题推荐-二手交易跳蚤市场微信小程序/安卓APP-项目实战

✨作者主页&#xff1a;IT毕设梦工厂✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Py…

通信原理板块——脉冲编码调制(PCM)

微信公众号上线&#xff0c;搜索公众号小灰灰的FPGA,关注可获取相关源码&#xff0c;定期更新有关FPGA的项目以及开源项目源码&#xff0c;包括但不限于各类检测芯片驱动、低速接口驱动、高速接口驱动、数据信号处理、图像处理以及AXI总线等 1、脉冲编码调制PCM原理 将模拟信号…

6.2 List和Set接口

1. List接口 List接口继承自Collection接口&#xff0c;List接口实例中允许存储重复的元素&#xff0c;所有的元素以线性方式进行存储。在程序中可以通过索引访问List接口实例中存储的元素。另外&#xff0c;List接口实例中存储的元素是有序的&#xff0c;即元素的存入顺序和取…

搭建成功simulink-stm32硬件在环开发环境

本次实验所使用的软件版本和硬件平台参数如下&#xff1a; Matlab版本: 2021b STM32硬件平台&#xff1a;YF_STM32_Alpha 1R4(参考自STM32 Nucleo F103RB官方开发板) YF_STM32_Alpha开发板 STM32 Nucleo F103RB 开发板 2.1 STM32硬件支持包下载 读者朋友平时使用的是和谐版M…

洗眼镜超声波清洗机用什么水清洗、小型超声波清洗机推荐

洗眼镜的超声波清洗机可以用清水清洗&#xff0c;因为超声波频率可以把眼镜缝隙中的脏污渍给振动出来&#xff0c;所以清水槽内放清水就可以了。像清洗一些机器零件的话&#xff0c;污渍比较重可以适当的家电清洁液进去清洗&#xff0c;这样清洗完的效果会比较好。戴眼镜的朋友…

Adobe家里的“3D“建模工 | Dimension

今天&#xff0c;我们来谈谈一款在Adobe系列中比肩C4D的高级3D软件的存在—— Dimension。 Adobe Dimension &#xff0c;其定位是一款与Photoshop以及Illustrator相搭配的3D绘图软件。 Adobe Dimensions与一般的3D绘图软件相较之下&#xff0c;在操作界面在功能上有点不大相同…

SDL2 播放音频数据(PCM)

1.简介 这里以常用的视频原始数据PCM数据为例&#xff0c;展示音频的播放。 SDL播放音频的流程如下&#xff1a; 初始化音频子系统&#xff1a;SDL_Init()。设置音频参数&#xff1a;SDL_AudioSpec。设置回调函数&#xff1a;SDL_AudioCallback。打开音频设备&#xff1a;SD…

NewStarCTF2023 Reverse Week3---Let‘s Go Wp

分析 程序打开后结合题目可以发现是 GO语言。 在GO语言中&#xff0c;main_init 要先于 main 函数运行。 在这里对一个iv做了处理。 用插件Signsrch发现AES加密 知道是AES后&#xff0c;就需要找密文&#xff0c;key和iv了。 密文应该就是前面的十六进制字符串。 key和i…

二百零三、Flume——Flume实时采集数据频率为1s的高频率Kafka数据直接写入ODS层表的HDFS文件路径下

一、目的 在离线数仓中&#xff0c;需要用Flume去采集Kafka中的数据&#xff0c;然后写入HDFS中。 由于每种数据类型的频率、数据大小、数据规模不同&#xff0c;因此每种数据的采集需要不同的Flume配置文件。玩了几天Flume&#xff0c;感觉Flume的使用难点就是配置文件 二、…

医疗项目的需求分析以及开发流程

一.项目的背景以及需求 1.项目背景 1.政策层面来看&#xff0c;近年来我国政府相关部门陆续颁发了支持数字医疗行业发展的相关政策&#xff0c;“互联网医疗”政策逐渐明确完善&#xff0c;为数字医疗行业发展提供支持&#xff0c;行业迎来政策福利期。 其次&#xff0c;从经济…

熟悉 Unity HDRP设置以提高性能

HDRP Version 10 了解如何利用高清晰度渲染管道(HDRP)设置&#xff0c;以最大限度地提高性能&#xff0c;并一次实现强大的图形。 随着Unity 2020 LTS及以后的HDRP版本10的发布&#xff0c;HDRP包继续优先考虑其用户友好的界面&#xff0c;灵活的功能&#xff0c;稳定性和总体…

第3章:搜索与图论【AcWing】

文章目录 图的概念图的概念图的分类有向图和无向图 连通性连通块重边和自环稠密图和稀疏图参考资料 图的存储方式邻接表代码 邻接矩阵 DFS全排列问题题目描述思路回溯标记剪枝代码时间复杂度 [N 皇后问题](https://www.luogu.com.cn/problem/P1219)题目描述全排列思路 O ( n ! …

什么是美国服务器,有哪些优势,适用于什么场景?

​  在互联网发展的过程中&#xff0c;服务器扮演着至关重要的角色。而美国作为全球信息技术的中心&#xff0c;其服务器在全球范围内受到广泛关注。  美国服务器是指在美国本土机房搭建并运行的服务器。其拥有带宽大、优质硬件、售后运维好、位置优越、数据安全性高以及免备…

《视觉SLAM十四讲》-- 后端 2

文章目录 09 后端 29.1 滑动窗口滤波和优化9.1.1 实际环境下的 BA 结构9.1.2 滑动窗口法 9.2 位姿图9.2.1 位姿图的意义9.2.2 位姿图优化 09 后端 2 9.1 滑动窗口滤波和优化 9.1.1 实际环境下的 BA 结构 由于计算机算力的限制&#xff0c;我们必须控制 BA 的规模&#xff0c…

Perl的LWP::UserAgent库爬虫程序怎么写

Perl的LWP::UserAgent库是一个用于发送HTTP请求的Perl模块。它可以用于编写Web爬虫、测试Web应用程序、自动化Web操作等。以下是一个简单的使用LWP::UserAgent库发送HTTP GET请求的Perl脚本的例子&#xff1a; #!/usr/bin/perluse strict; use warnings; use LWP::UserAgent;# …

WPF中的虚拟化是什么

WPF&#xff08;Windows Presentation Foundation&#xff09;中的虚拟化是一种性能优化技术&#xff0c;它主要用于提高大量数据展示的效率。在WPF中&#xff0c;如果你有一个包含大量项的ItemsControl&#xff08;例如ListBox、ListView或DataGrid等&#xff09;&#xff0c;…