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.mfbz.cn/a/144741.html

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

相关文章

数字化时代的甜蜜梦境,拓世AI女友带你探索全新互动体验

只要花42块钱&#xff0c;就能被6个大美女疯狂倒追&#xff0c;听起来像科幻文对吧&#xff1f; 最近&#xff0c;一款真人互动游戏《完蛋&#xff01;我被美女包围了&#xff01;》&#xff08;以下简称《完蛋》&#xff09;爆火&#xff0c;不仅霸榜Steam国内畅销榜首&#…

python的高性能web应用的开发与测试实验

引言 python语言一直以开发效率高著称&#xff0c;被广泛地应用于自动化领域&#xff1a; 测试自动化运维自动化构建发布自动化 但是因为其也具有如下两个特征&#xff1a; 解释型语言GIL全局解释器锁 前者导致其性能天然就被编译型语言在性能上落后了许多。而后者则在多核…

美国站群服务器IP如何设置分配?

​  在配置美国站群服务器时&#xff0c;IP的分配是一个重要的步骤。下面将介绍一些关于美国站群服务器IP分配的相关知识。 独享IP和虚拟IP 在租用美国站群服务器之前&#xff0c;我们需要了解提供的IP是独享的还是虚拟的。独享IP指每个网站都有独立的IP地址&#xff0c;而虚…

如何制作出高级感满满的的照片书

随着数码相机的普及&#xff0c;越来越多的人喜欢将生活中的点滴美好记录下来&#xff0c;其中照片书就是一种非常受欢迎的方式。但是&#xff0c;如何制作出高级感满满的“照片书”呢&#xff1f;今天&#xff0c;我们就来分享几个小技巧&#xff0c;帮助你轻松打造出令人惊艳…

VEX —— Half-edges

目录 一&#xff0c;概述 二&#xff0c;等效 三&#xff0c;函数 在一些VEX函数&#xff0c;可将边看成为每个面非共享的半边&#xff1b; 一&#xff0c;概述 在houdini&#xff0c;边通常被视为面之间无方向且共享的&#xff0c;然而&#xff0c;对于一些任务&#xff08…

【云计算网络安全】DDoS 攻击类型:什么是 ACK 洪水 DDoS 攻击

文章目录 一、什么是 ACK 洪水 DDoS 攻击&#xff1f;二、什么是数据包&#xff1f;三、什么是 ACK 数据包&#xff1f;四、ACK 洪水攻击如何工作&#xff1f;五、SYN ACK 洪水攻击如何工作&#xff1f;六、文末送书《AWD特训营》内容简介读者对象 一、什么是 ACK 洪水 DDoS 攻…

玩转ChatGPT:ARIMA模型定制GPT-1.0

一、写在前面 好久不更新咯&#xff01; OpenAI又推出了GPT的一系列重大更新&#xff0c;其中GPTs深得我心啊。 GPTs允许用户创建自定义的ChatGPT版本&#xff0c;以满足自己各种特定需求。其核心理念在于&#xff0c;用户可以为不同的场景和任务创建定制化的ChatGPT。这意味…

超详细!必看!!STM32--系统滴答SysTick

一、SysTick是什么&#xff1f; Systick定时器是一个24bit的倒计时&#xff08;向下计数&#xff09;定时器&#xff0c;功能就是实现简单的延时。 SysTick 是一种系统定时器&#xff0c;通常在嵌入式系统中使用。它是 ARM Cortex-M 处理器的一个特殊定时器&#xff0c;用于提…

制作这种在线宣传画册,可轻松收获客户!

制作企业宣传画册&#xff0c;首先要了解企业制作宣传画册的需求以及展示方向&#xff0c;如今互联网时代&#xff0c;宣传画册的制作也应该要创新&#xff0c;而制作一本在线电子宣传画册用于线上宣传是非常有必要的。如何制作呢&#xff1f; 我们 可以使用FLBOOK平台在线制作…

CVE-2023-2766:泛微E-Office信息泄露漏洞复现 [附POC]

文章目录 泛微E-Office信息泄露漏洞(CVE-2023-2766)复现 [附POC]0x01 前言0x02 漏洞描述0x03 影响版本0x04 漏洞环境0x05 漏洞复现1.访问漏洞环境2.构造POC3.复现 0x06 修复建议 泛微E-Office信息泄露漏洞(CVE-2023-2766)复现 [附POC] 0x01 前言 免责声明&#xff1a;请勿利用…

WGCLOUD的特点整理

做运维工作很多年了&#xff0c;项目中用过不少的运维软件工具&#xff0c;今天整理下WGCLOUD的特点&#xff08;优点&#xff09; 首先WGCLOUD是完全免费的 部署使用&#xff1a;部署简单方便&#xff0c;上手容易&#xff0c;几乎没有学习成本&#xff0c;对新手友好 文档…

OSG项目:模仿Ventsim制作三维矿井智能通风系统

1、效果 2、计划内容 1) 三维场景的加载显示;已实现 2)矿井巷道建模及纹理;已实现 3)矿井基础数据采集及修正;已实现 4)通风网络解算算法;已实现 5)通风设备及设施模型制作;未实现 6)风流模拟效果 ;已实现 7)火灾模拟效果;未实现

python读取txt格式的点云文件,可视化显示,保存ply格式

在计算机视觉和三维几何处理中&#xff0c;点云是一种重要的数据表示形式。点云由许多离散的点组成&#xff0c;每个点都有三维坐标&#xff0c;通常还包括其他信息。 空格形式的点云文件 import open3d as o3ddef read_txt_space(txt_file):# 读取点云数据pcd o3d.io.read_p…

应用disruptor队列-ringBuffer环形缓冲器

一disruptor介绍 Disruptor是一个高性能的消息框架&#xff0c;其核心是基于环形缓冲区实现的。Disruptor的设计目标是尽可能地减少线程之间的竞争和同步&#xff0c;从而提高系统的吞吐量和响应速度。下面让我来介绍一下在使用Disruptor中如何优雅地使用环形队列。 首先&…

react Antd3以下实现年份选择器 YearPicker

项目antd版本低&#xff0c;没有直接可使用的年份选择器&#xff0c;参考此篇&#xff08;使用antd实现年份选择器控件 - 掘金&#xff09; 一开始在state里设置了time&#xff1a; this.state {isopen: false,time: null } 在类似onChange事件里this.setState({time: valu…

如何进行iOS技术博客的备案?

如何进行iOS技术博客的备案&#xff1f; 标题&#xff1a;iOS技术博客备案流程及要求解析 摘要&#xff1a; 在本篇问答中&#xff0c;我们将为iOS技术博主介绍如何进行备案。如果你的iOS应用只包含简单的页面&#xff0c;并通过蓝牙进行数据采集和传输&#xff0c;那么你可能…

苹果手机照片如何导入电脑?无损快速的传输办法分享!

前些天小编的朋友联系到我&#xff0c;说是自己苹果手机里面的照片太多&#xff0c;有好几千张&#xff0c;不知道该怎么快而无损地传到电脑。我想遇到这种情况的不止是小编的朋友&#xff0c;生活中遇到手机照片导入电脑的同学不在少数。不管是苹果手机还是安卓手机&#xff0…

ef core code first pgsql

在使用efcode来操作pgsql的时候&#xff0c;总有些基础配置流程项目建立完之后后面就很少用&#xff0c;总是忘掉&#xff0c;写个文档记忆一下吧。基于net 6.0。 1.创建一个mvc项目和一个EF类库 2.在类库里面安装依赖dll Microsoft.EntityFrameworkCore.Design 需要添加的…

ARM64 linux并发与同步之经典自旋锁

1.3 经典自旋锁 在实际项目中临界区数据有可能会修改一个数据结构或者链表中的数据&#xff0c;在整个过程中要保证原子性&#xff0c;才不会影响数据的有效性&#xff0c;这个过程使用原子变量不合适&#xff0c;需要使用锁机制来完成&#xff0c;自旋锁&#xff08;spinlock&…

【Mycat2实战】一、Mycat简介

1. 什么是Mycat 什么是Mycat Mycat是数据库中间件&#xff0c;所谓中间件数据库中间件是连接Java应用程序和数据库中间的软件。 为什么要用Mycat 遇到问题&#xff1a; Java与数据库的紧耦合高访问量高并发对数据库的压力读写请求数据不一致 2. Mycat与其他中间件区别 目前的…
最新文章