【Vue3源码】第三章 readonly详解 从零实现Vue3 readonly API

news2024/11/15 19:47:48

【Vue3源码】第三章 readonly详解 从零实现Vue3 readonly API

前言

上一章节我们实现了effect函数的stop和onstop功能,至此effect函数源码的编写就暂时告一段落了,这一章我们继续解读Vue3源码,开始实现Vue3 Reactivity :core 中的readonly API,并且优化之前写的reactive API。

在这里插入图片描述

实现readonly

readonly函数相信你在学习vue3的初始api时一定看到过,很简单的功能就是让调用这个函数的响应式对象只读,也就是无法set操作。

1.我们先看下单元测试代码

import { readonly } from "../reactive";

describe("readonlu", () => {
  it("happy path", () => {
    // not set
    const original = { foo: 1, bar: { baz: 2 } };
    const wrapped = readonly(original)
    expect(wrapped).not.toBe(original)
    expect(wrapped.foo).toBe(1)
  });
});

2.实现readonly

在reactive.ts文件下导出这个函数

export const readonly = (raw) => {
    return new Proxy(raw,{
        get(target,key,receiver) {
            const res =  Reflect.get(target,key,receiver)
            return res

        },
        set(target,key,value,receiver) {
            return true
        },

    })
}

readonly真的很简单,我们的目的是要优化这些函数,readonly和reactive函数的代码高度相似,vue3就重构抽离了这些代码,来增加代码可读性。

3.优化reactive和readonly

reactive和readonly两个函数中代码和逻辑实在太相似了,如果重构抽离相似代码的话,就一点也不优雅了!

优化前:
在这里插入图片描述
所以我们要在原基础上对代码重构并且优化,让它们变得更加优雅~

优化后:
在这里插入图片描述

优雅~实在是太优雅了!!

3.1 重构get和set操作

vue3把get放入到了一个高阶函数creatGetter中,高阶函数返回get,set也同理,我们现在来封装一下:

function createGetter(isReadonly = true) {
  return function get(target, key) {
    const res = Reflect.get(target, key)
    if (isReadonly) {
      track(target, key)
    }
    return res
  }
}

function createSetter() {
  return function set(target, key, value, receiver) {
    const res = Reflect.set(target, key, value, receiver)
    trigger(target, key)
    return res
  }
}

export const reactive = (raw) => {
    return new Proxy(raw,  {
  		get: createGetter(),
  		set: createSetter()
	})
}

export const readonly = (raw) => {
    return new Proxy(raw, {
        get: createGetter(false),
  		set(target, key, value, receiver) {
    		return true
  		},
    })
}

其中get我们可以通过给定一个传参去判断是否是readonly,而readonly的set有些特殊我们先不进行修改~

但是这个代码还是不够美观,继续优化

3.2 baseHandlers

在src目录下新建一个baseHandlers.ts文件
并且把之前封装好的creatGetter,creatSetter提取到这个文件夹中

import { track, trigger } from "./effect"

function creatGetter(isReadonly = true) {
  return function get(target, key) {
    const res = Reflect.get(target, key)
    if (isReadonly) {
      track(target, key)
    }
    return res
  }
}

function creatSetter() {
  return function set(target, key, value, receiver) {
    const res = Reflect.set(target, key, value, receiver)
    trigger(target, key)
    return res
  }
}


//返回这个对象
export const mutableHandlers = {
  get: creatGetter(),
  set: creatSetter()
}

export const readonlyHandlers = {
  get: creatGetter(false),
  set(target, key, value, receiver) {
    return true
  },
}

然后我们再去reactive中引入mutableHandlers,readonlyHandlers即可

3.3 createActiveObject函数

为了语义化vue3又把new Proxy的操作也抽离成了createActiveObject函数

import { mutableHandlers, readonlyHandlers } from "./baseHandlers"



export const reactive = (raw) => {
    return createActiveObject(raw, mutableHandlers)
}

export const readonly = (raw) => {
    return createActiveObject(raw, readonlyHandlers)
}

function createActiveObject(raw,readonlyHandlers) {
    return new Proxy(raw, readonlyHandlers)
}
3.4 优化get和set捕获器

我们回到baseHandlers.ts文件里来,最后一步优化

提问:我们有必要每次生成proxy对象时都去creatGetter和creatSetter 生成get和set吗?

显示是没有必要的,所以我们可以利用缓存技术,继续优化代码

const get = createGetter()
const set = createSetter()
const readonlyGet = createGetter(false)

export const mutableHandlers = {
  get,
  set
}

export const readonlyHandlers = {
  get: readonlyGet,
  set(target, key, value, receiver) {
    return true
  },
}
3.5 警告用户不能set操作

这还没有结束,我们的readonly还差一个触发set操作时,警告用户无法set的功能

export const readonlyHandlers = {
  get: readonlyGet,
  set(target, key, value, receiver) {
    console.warn(`key:${key} set失败,因为target是readonly的`,target)
    return true
  },
}

新增一个单元测试,测试:用户进行set操作时警告用户无法set操作!

it("warn then call set", () => {
    console.warn = jest.fn()
    const user = readonly({
      age: 10
    })
    user.age = 11
    expect(console.warn).toBeCalled()
  })

成功通过所有测试了!
在这里插入图片描述

下节预告《vue3源码实现 isReactive 和 isReadonly》

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

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

相关文章

Java基础361问14问——为什么非静态内部类会默认持有外部类的引用?

在内存泄露问题排查中最常遇到就是 【非静态内部类默认持有外部类的引用】 文章目录1 字节码分析javac Activity.javajavap -c Activity.class2 静态内部类会持有外部类引用吗?参考文档// 简化处理相关代码 public class Activity {private Handler inner new Handler();priv…

C++面向对象(中)

文章目录前言1.类的6个默认成员函数介绍2.构造函数3.析构函数1.概念2.析构函数特征4.拷贝构造1.概念2.拷贝构造函数特征3.注意事项5.赋值运算符重载1.概念6.补充知识const成员函数7.取地址运算符和const取地址运算符重载8.总结前言 本文主要介绍C中的六个天选之子,…

MicroBlaze系列教程(6):AXI_IIC的使用

文章目录 @[toc]AXI_IIC简介MicroBlaze硬件配置常用函数使用示例波形实测参考资料工程下载本文是Xilinx MicroBlaze系列教程的第6篇文章。 AXI_IIC简介 一般情况下,使用FPGA实现I2C协议主要有两种方式:一种是基于Verilog实现起始位、停止位、ACK产生和判断、数据的发送和接收…

程终止、进程睡眠、进程对信号处理过程中等的方法

上一章学习了调度的方式,分为主调度器和周期性调度器,明白了进程切换分为自愿(voluntary)和强制(involuntary)两种。 自愿切换: 是指任务由于等待某种资源,将state改为非running状态后,主动调用schedule让出CPU 任务…

html中元素居中的五种方法

在网页开发中,经常会有嵌套元素中将子元素居中的要求。下边将五种常用的居中方法进行总结。 1:原始图(父子元素无border,无padding): 2:实现居中效果: 一:使用margin…

一篇文章带你学会Anisble中的如何处理失败任务

目录 一、循环 1、简单循环 2、循环散列或字典列表 3、练习 二、条件 三、触发器 四、处理失败任务 1、ignore_errors 2、force_handlers 3、changed_when 4、failed_when 5、block 练习 一、循环 作用:循环迭代任务 1、简单循环 loop: ##赋值列表 -…

[软件工程导论(第六版)]第4章 形式化说明技术(复习笔记)

文章目录4.1 概述4.2 有穷状态机4.3 Petri网4.4 Z语言按照形式化程度,可以把软件工程使用的方法划分成非形式化、半形式化、形式化三类非形式化方法:使用自然语言描述需求规格说明半形式化方法:使用数据流图或实体-联系图建立模型形式化方法&…

P2P视频聊天技术分析

整个P2P视频过程需要知道双方的媒体类型、流和候选者,所以这里就会用到一下技术: ​ 信令服务器socket.io ​ 状态机 ​ ICE服务器 ​ WebRTC框架 ​ 媒体协商 信令服务器Socket.io 信令服务器说白了作用就是发消息的中转站,A把msg发到…

网络流与图(二)

上一节我们讲到了退化圈方向搜索算法,它能得到全局最优解。然而算法运行过程中需要选择一个可行改进圈方向,对于一个大型网络流来说,这并非容易的。我们需要找到在每次循环中确认可行改进圈方向或者证明不存在的方法。我们现在就来探讨这个问…

Andriod入门级开发

这学期有个课设,我们组我负责一个手机APP的开发,虽然刚开始说要实现什么智能导航,类似高德地图那种,但最后阉割的只剩一个Socket通信了,因为之前没有接触过(可能之后也不会再接触),记…

【数据管理】谈谈哈希原理和散列表

一、说明 提起哈希,有人要说:不就是一个稀疏表格么,谈的上什么原理?我说:非也,哈希是是那种看似无物,其实解决大问题的东西。如何提高数据管理效率?这是个问题,随着这个问…

测试2:编写测试用例的方法

2.编写测试用例的方法 7种 测试常用的方法:code review 代码静态分析、CI/CD CI–持续集成–开发成员经常集成它们的工作,尽快发现集成错误 CD–持续部署–将集成后的代码部署到更贴近真实运行的环境 2.1 测试用例的描述: 用例编号 用例…

Python纯Numpy手撕SGD

文章目录简介问题建模数据加载和预处理数据加载预处理分batch损失函数训练运行简介 本博客用多元线性回归展示如何从零实现一个随机梯度下降SGD, 不使用torch等AI框架 问题建模 给定一个数据集X∈RN(D1)\large X \in \R^{N \times (D1)}X∈RN(D1)和对应标签向量Y∈RN\large …

centos7防火墙工具firewall-cmd使用

centos7防火墙工具firewall-cmd使用防火墙概述centos7防火墙工具firewall-cmd使用介绍firewalld的基本使用服务管理工具相关指令配置firewalld-cmd防火墙概述 防火墙是可以帮助计算机在内部网络和外部网络之间构建一道相对隔绝的保护屏障,从而保护数据信息的一种技…

Vulnhub 渗透练习(七)—— FRISTILEAKS: 1.3

环境搭建 下载链接 virtualbox 打开靶机设置为 host-only,攻击机同样。 具体可点此处 信息收集 开了个 80 端口。 用的是 apache 2.2.15 ,这个版本有个解析漏洞。 目录 根据首页的图片猜测 /fristi/ 目录(不过我没想到 -_-&#x…

由浅入深掌握各种 Python multiprocessing 进程间通信方式

由浅入深掌握各种 Python 多进程间通信方式1、为什么要掌握进程间通信2、进程间各类通信方式简介3、消息机制通信1) 管道 Pipe 通信方式2) 消息队列Queue 通信方式4、同步机制通信(1) 进程间同步锁 – Lock(2) 子进程间协调机制 -- Event5、共享内存方式通信(1) 共享变量(2) 共…

【Python】控制自己的手机摄像头拍照,并自动发送到邮箱

前言 嗨喽,大家好呀~这里是爱看美女的茜茜呐 今天这个案例,就是控制自己的摄像头拍照, 并且把拍下来的照片,通过邮件发到自己的邮箱里。 想完成今天的这个案例,只要记住一个重点:你需要一个摄像头 思路…

Android 7.0 OTA升级(高通)

文章目录1. Full OTA 方式升级介绍1.1 Full OTA 制作第一步:生成 msm89xx-target_files-eng.XXX.zip1.2 Full OTA 制作第二步:Modem 等非 HLOS 加入升级包的方法1.3 Full OTA 制作第三步:生成 update.zip 升级包2. Incremental OTA 方式升级介…

Android 基础知识4-2.6LinearLayout(线性布局)

一、LinearLayout的概述 线性布局(LinearLayout)主要以水平或垂直方式来排列界面中的控件。并将控件排列到一条直线上。在线性布局中,如果水平排列,垂直方向上只能放一个控件,如果垂直排列,水平方向上也只能…

Java基础-xml

1.xml 1.1概述 万维网联盟(W3C) 万维网联盟(W3C)创建于1994年,又称W3C理事会。1994年10月在麻省理工学院计算机科学实验室成立。 建立者: Tim Berners-Lee (蒂姆伯纳斯李)。 是Web技术领域最具权威和影响力的国际中立性技术标准机构。 到目前为止&#…