解读vue3源码-响应式篇2

提示:看到我 请让我滚去学习

文章目录

  • vue3源码剖析
  • reactive
    • reactive使用proxy代理一个对象
      • 1.首先我们会走isObject(target)判断,我们reactive全家桶仅对对象类型有效(对象、数组和 Map、Set 这样的集合类型),而对 string、number 和 boolean 这样的 原始类型 无效。
      • 2.判断是否已经代理,返回对象本身。
      • 3.判断在对象的代理对象在weakmap中是否已经存在,是的话返回存储的代理对象
      • 4.判断对象是否为可扩展对象
      • 5.使用new proxy代理对象
    • reactive使用proxy代理的陷阱封装
      • get陷阱代码分析
        • 1.首先在get方法中局部化isReadonly和isShallow标识,然后走ifelse判断,返回相应的值
        • 2.判断是目标对象是否是数组,如果是数组,并且访问的是数组的一些方法,那么返回对应的方法
        • 3.判断是否访问对象上的hasOwnProperty属性,返回对象原型上的方法,并收集依赖
        • 4.最后如果是内置stmbol或者是不可追踪的key直接返回res,不进行依赖收集
        • 5.如果不是只读调用,进行依赖收集触发track
        • 6.如果是浅层代理不需要对访问的属行进行深层代理,返回访问属性值
        • 7.访问属性若是对象,那么就递归将子元素也变成代理对象
      • set陷阱分析
        • 1.拿取当前值和旧值,判断是否目标对象是只读对象,若是不做任何处理返回false
        • 2.通过hadKey判断操作类型类型是修改旧属性,还是新增属性,在副作用函数触发时做不同处理
        • 3.对比新旧值,触发依赖收集
      • deleteProperty、has、ownKeys陷阱
    • track函数
    • trigger函数


vue3源码剖析

vue代码以模块形式放置在packages文件夹下,分模块打包可以使用treesharking,可以在项目中只应用需要的模块,甚至我们可以只使用单一模块实现相应功能,例如我只使用reactive模块实现和拓展响应式数据。(monorepo)

在这里插入图片描述

reactive

我们这次学习的响应式相关api都在reactive文件夹中,那么就让我们看看reactive-api在vue中是怎么实现的:

reactive使用proxy代理一个对象

在这里插入图片描述
在reactive文件中我们以上4个方法都是使用createReactiveObject高阶函数实现,参入不同的方法。这是因为我们vue官网的reactive、shallowReactive、redonly、shallowReadonly都是使用这个方法实现的,让我们看看这个函数做了什么处理
这个函数传入5个参数
target:目标对象target,
isReadonly:布尔值isReadonly表示是否创建只读对象,
baseHandlers:基础处理器baseHandlers用于普通对象的代理处理,
collectionHandlers:集合处理器collectionHandlers专门用于处理如数组和Map等集合类型的代理
proxyMap:用于存储代理映射的WeakMap
在这里插入图片描述

1.首先我们会走isObject(target)判断,我们reactive全家桶仅对对象类型有效(对象、数组和 Map、Set 这样的集合类型),而对 string、number 和 boolean 这样的 原始类型 无效。

因为底层使用proxy代理,proxy只能代理对象,确定目标是否为可被观察的类型,如果代理目标不是对象直接返回目标本身,dev环境控制台warn
在这里插入图片描述

2.判断是否已经代理,返回对象本身。

isReadonly:传入参数是否只读
target[ReactiveFlags.IS_REACTIVE]:判断对象是否已经被代理,代理对象会拦截ReactiveFlags中属性(特殊字符串),如果有值说明已被代理
在这里插入图片描述
在这里插入图片描述

ReactiveFlags是vue在对象上的标识,我们可以在传入目标上直接加上相应属性,会影响数据的绑定,例如:
在这里插入图片描述
上图这样会影响reactive的正确执行。

3.判断在对象的代理对象在weakmap中是否已经存在,是的话返回存储的代理对象

在这里插入图片描述

4.判断对象是否为可扩展对象

getTargetType函数会根据传入对象返回相应code码

  • 1------传入对象是Object或者Array类型
  • 2------传入对象是map、set类型
    这两者在proxy陷阱中处理方式不同
  • 0------传入对象是不可扩展对象,那么就不用代理

Object.isExtensible(value) 方法会返回 true 当:

对象是可扩展的:默认情况下,JavaScript 中的对象是可扩展的,这意味着你可以向它们添加新的属性。如果一个对象没有被冻结(Object.freeze())或密封(Object.seal()),那么 Object.isExtensible(value) 将返回 true。Object.freeze():(不可写,不可配置,可枚举,不可描述) Object.freeze()方法可以冻结一个对象。
对象没有被设置为不可扩展:如果对象在创建后没有通过 Object.preventExtensions() 方法使其变得不可扩展,那么它依然可扩展。
对象不是原始值:Object.isExtensible() 只能用于对象,如果 value 是一个原始值(如字符串、数字、布尔值、null 或 undefined),该方法会抛出错误,因此在这种情况下不会返回 true 或 false。
在这里插入图片描述
markRaw()-api在Vue3.0中的作用是标记一个对象,使其永远不会再成为响应式对象。其给对象属性赋值ReactiveFlags.SKIP为true,那么再使用reactive给次对象做响应式时,默认就会识别为不可扩展对象,不会在做响应式代理
在这里插入图片描述

5.使用new proxy代理对象

通过targetType === TargetType.COLLECTION判断对象是否为集合类型,走collectionHandlers或者baseHandlers陷阱函数,并将代理对象存储在proxyMap中
在这里插入图片描述
到这里我们得到了proxy对象,那么接下来我们看看传入的这个baseHandlers做了什么

reactive使用proxy代理的陷阱封装

baseHandlers即传入函数mutableHandlers
在这里插入图片描述在这里插入图片描述
在这里插入图片描述

get陷阱代码分析

MutableReactiveHandle对proxy的get、set、deleteProperty、has、ownKeys陷阱做了相应代理(这些方法我们上一篇都介绍过),继承了BaseReactiveHandler公用类,事实上 mutableHandlers,readonlyHandlers,shallowReactiveHandlers, shallowReadonlyHandlers都继承自这个基础类,这个基础类定义了公用的get陷阱,依照我们上一篇实现的简易版本的reactive中,我们猜测陷阱一个做了两件事:1.返回访问值 2.收集依赖,源码如下
在这里插入图片描述

1.首先在get方法中局部化isReadonly和isShallow标识,然后走ifelse判断,返回相应的值

这也就是我们上面代理时为什么可以从ReactiveFlags特殊属性做判断,可以看出是在这里对特殊属性做了相应拦截
在这里插入图片描述

2.判断是目标对象是否是数组,如果是数组,并且访问的是数组的一些方法,那么返回对应的方法

在这里插入图片描述
Vue3中使用arrayInstrumentations对数组的部分方法做了处理,为什么要这么做呢?
在这里插入图片描述
这个方法可以分为2部分:

  • 1.对includes、indexOf、lasetindexOf方法进行拦截重写,先调用了数组原型方法进项查找,如果没找到将查找对象的原型又查找了一次,为什么这么做?我们来看示例代码
1.
let obj={}
let arr=reactive([obj])
console.log(arr.includes(obj)) ///不重写includes方法输出 false我们在代理对象arr中去查找obj原始数据,但是reactive在代理[obj]也会递归把obj对象进行代理,这样会导致arr中存储的其实是proxy对象,在arr中找obj会找不到,所以要把arr使用toRaw在arr原始数据上找2.
let obj = {a:1}
let obj2= reactive(obj)
let arr = reactive([obj])
console.log(arr.includes(obj2)) ///不重写includes方法输出 false然后如果是这种在arr原型上也是obj原始数据,找代理对象obj2也找不到会进入逻辑res==-1||res==false,将obj2也使用toRaw得到原始数据再次查找一遍。
  • 2.对"push", “pop”, “shift”, “unshift”, "splice"方法进行重写,上一篇中我们提到这些方法会隐式的修改数组长度,而这就会触发length的收集依赖,这显然不是我们想要的,所以在运行这些方法时需要暂停依赖收集
3.判断是否访问对象上的hasOwnProperty属性,返回对象原型上的方法,并收集依赖

在这里插入图片描述

4.最后如果是内置stmbol或者是不可追踪的key直接返回res,不进行依赖收集

这一步是为了过滤一些特殊的属性,例如原生的Symbol类型的属性,如:Symbol.iterator、Symbol.toStringTag等等,这些属性不需要进行依赖收集,因为它们是内置的,不会改变;还有一些不可追踪的属性,如:proto、__v_isRef、__isVue这些属性也不需要进行依赖收集;
在这里插入图片描述

5.如果不是只读调用,进行依赖收集触发track

在这里插入图片描述

6.如果是浅层代理不需要对访问的属行进行深层代理,返回访问属性值

在这里插入图片描述### 6.如果是浅层代理不需要### 6.如果访问属性是一个已经使用ref代理的对象对属性值进行.value结构
在这里插入图片描述

7.访问属性若是对象,那么就递归将子元素也变成代理对象

在这里插入图片描述

set陷阱分析

当我们看完set我们知道它主要做了访问数据返回和依赖收集,那么我们之前实现的set中应该是数据修改和副作用函数触发

1.拿取当前值和旧值,判断是否目标对象是只读对象,若是不做任何处理返回false

在这里插入图片描述

2.通过hadKey判断操作类型类型是修改旧属性,还是新增属性,在副作用函数触发时做不同处理

在这里插入图片描述

3.对比新旧值,触发依赖收集

在这里插入图片描述

(target === toRaw(receiver))此处判断如果目标是原创原型链中的某个上游,则不要触发。

例如  
const obj={}
const proto={bar:1}
const child=reactive(obj)
const parent=reactive(proto)
Object.setPrototypeOf(child, parent)
effect(()=>{console.log("🚀 ~ child:", child.bar)})
child.bar = 2 
//🚀 ~ child:",1
//🚀 ~ child:",1
//在effect访问child.bar,child不存在就去原型链找,找到parent.bar,但是parent是响应式对象,这样parent.bar和effect就建立联系了,所以第一次依赖收集收集了child.bar和parent.bar。而对对象设置属性,如果对象不存在此属性,就会找到这个对象的原型,触发原型的[set]内部方法,即parent的[set]方法,所以会被拦截到,这样就解释了为什么会触发2次

deleteProperty、has、ownKeys陷阱

deleteProperty、has陷阱都是常规去收集和触发副作用函数,而ownKeys是有些特别的。
ownKeys在对象或数组for…in遍历时触发,而我们遍历重新触发的条件为数组或对象key长度改变,对象变量在get中我们可以清楚的知道我们要获取的是哪个属性,但是ownKeys中并不能,所以我们在track函数传入ITERATE_KEY作为key
在这里插入图片描述

    const data = [1,2,3,4,{a:111}]const obj = reactive(data)watchEffect(function effectFn111 () {console.log('11111')for (const key in obj) {}})obj.a=6//11111//11111

tigger函数中,对象新增和删除属性都会影响for…in,for…in依赖的对象key为ITERATE_KEY,所以都要重新执行ITERATE_KEY的副作用函数执行,当判断对象新增删除值时都要重新执行key为ITERATE_KEY的副作用函数,即重新运行for…in存在的副作用函数
在这里插入图片描述

到此我们reactive使用了new Proxy代理了对象,返回了一个代理对象,实现了对对象属性访问、更改的拦截,那我们在看下track(依赖收集)和trigger函数(依赖触发)

track函数

track函数就如我们上一篇中将对象-对象属性map-efftct副作用函数map关联存储在了targetMap全局的weakMap中,结构我们非常熟悉。
在这里插入图片描述
在这里插入图片描述
其中值得一提的是在创建dep时使用的是createDep,这个方法如下,这是为了给dep上挂载一个清除自身的函数。例如当我们这个属性的effects依赖为0时,即这个dep没有依赖,那么我们就可以从调用此方法将属性从tagerMap表上面将其删除。

在这里插入图片描述

当然还有一些其他操作,例如shouldTrack判断是否收集依赖,我们上面重写数组push等方法是会暂停收集,就是pauseTracking函数更改了这个全局属性来暂停依赖收集,resetTracking重新开启收集依赖。还有一些其他参数,和effect函数相关,我们看effect函数时在细说
在这里插入图片描述
在这里插入图片描述

trigger函数

tigger函数会读取 track函数收集到的,在访问属性上绑定的effect副作用函数,循环执行,这样当前修改属性所有依赖都会重新执行更新。
在这里插入图片描述在这里插入图片描述
当然在其中也会有一些其他操作例如我们上一篇说数组直接修改length属性,会隐式的改变数组内元素,那么就需要修改属性’length’时,对于数组中所有索引大于等于新长度的元素全部进行副作用触发,还有执行元素新增、删除操作时触发ITERATE_KEY(即对象for…in循环)收集的副作用函数。
还有一些其他参数,和effect函数相关,我们看effect函数时在细说

在这里插入图片描述


总结:vue3的reactive能够代理对象,reactive、shallowReactive、redonly、shallowReadonly都是使用同一个高阶函数实现,在数据访问时收集依赖,数据修改时触发依赖重新执行。其中做了很多的操作判断,保证其能够正常运行,例如对数组一些方法的特殊等。

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

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

相关文章

【数学建模】多波束测线问题(持续更新)

多波束测线问题 问题 1建立模型覆盖宽度海水深度重叠长度重叠率 问题二问题三问题四 问题 1 与测线方向垂直的平面和海底坡面的交线构成一条与水平面夹角为 α \alpha α的斜线(如下图),称 α \alpha α为坡度。请建立多波束测深的覆盖宽度及…

Python代码,强化学习,深度学习

python代码编写,Python算法设计,强化学习优化,改进模型,训练模型,测试模型,可视化绘制,代编运行结果,交互多模型改进,预测模型,算法修改,Python包…

11.斑马纹列表 为没有文本的链接设置样式

斑马纹列表 创建一个背景色交替的条纹列表。 使用 :nth-child(odd) 或 :nth-child(even) 伪类选择器,根据元素在一组兄弟元素中的位置,对匹配的元素应用不同的 background-color。 💡 提示:你可以用它对其他 HTML 元素应用不同的样式,如 <div>、<tr>、<p&g…

利用PyTorch进行模型量化

利用PyTorch进行模型量化 目录 利用PyTorch进行模型量化 一、模型量化概述 1.为什么需要模型量化&#xff1f; 2.模型量化的挑战 二、使用PyTorch进行模型量化 1.PyTorch的量化优势 2.准备工作 3.选择要量化的模型 4.量化前的准备工作 三、PyTorch的量化工具包 1.介…

openGauss学习笔记-312 openGauss 数据迁移-MySQL迁移-迁移MySQL数据库至openGauss-概述

文章目录 openGauss学习笔记-312 openGauss 数据迁移-MySQL迁移-迁移MySQL数据库至openGauss-概述312.1 工具部署架构图 openGauss学习笔记-312 openGauss 数据迁移-MySQL迁移-迁移MySQL数据库至openGauss-概述 312.1 工具部署架构图 当前openGauss支持对MySQL迁移服务&#x…

【多任务YOLO】 A-YOLOM: You Only Look at Once for Real-Time and Generic Multi-Task

You Only Look at Once for Real-Time and Generic Multi-Task 论文链接&#xff1a;http://arxiv.org/abs/2310.01641 代码链接&#xff1a;https://github.com/JiayuanWang-JW/YOLOv8-multi-task 一、摘要 高精度、轻量级和实时响应性是实现自动驾驶的三个基本要求。本研究…

多光谱的空间特征和光谱特征Statistics of Real-World Hyperspectral Images

文章目录 Statistics of Real-World Hyperspectral Images1.数据集2.spatial-spectral representation3.Separable Basis Components4.进一步分析5.复现一下5.1.patch的特征和方差和论文近似&#xff0c;5.2 spatial的basis和 spectral的basis 6.coef model7.join model Statis…

多视角数据的不确定性估计:全局观的力量

论文标题&#xff1a;Uncertainty Estimation for Multi-view Data: The Power of Seeing the Whole Picture 中文译名&#xff1a;多视角数据的不确定性估计:全局观的力量 原文地址&#xff1a;Uncertainty Estimation for Multi-view Data: The Power of Seeing the Whole …

python用selenium网页模拟时xpath无法定位元素解决方法2

有时我们在使用python selenium xpath时&#xff0c;无法定位元素&#xff0c;红字显示no such element。上一篇文章写了1种情况&#xff0c;是包含iframe的&#xff0c;详见https://blog.csdn.net/Sixth5/article/details/140342929。 本篇写第2种情况&#xff0c;就是xpath定…

类和对象:赋值函数

1.运算符重载 • 当运算符被⽤于类类型的对象时&#xff0c;C语⾔允许我们通过运算符重载的形式指定新的含义。C规定类类型对象使⽤运算符时&#xff0c;必须转换成调⽤对应运算符重载&#xff0c;若没有对应的运算符重载&#xff0c;则会编译报错&#xff1b;&#xff08;运算…

数据旋律与算法和谐:LLMs的微调交响

论文&#xff1a;https://arxiv.org/pdf/2310.05492代码&#xff1a;暂未开源机构&#xff1a;阿里巴巴领域&#xff1a;模型微调发表&#xff1a;ACL 2024 这篇论文《How Abilities in Large Language Models are Affected by Supervised Fine-tuning Data Composition》深入…

【BUG】已解决:raise KeyError(key) from err KeyError: (‘name‘, ‘age‘)

已解决&#xff1a;raise KeyError(key) from err KeyError: (‘name‘, ‘age‘) 欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 欢迎来到我的主页&#xff0c;我是博主英杰&#xff0c;211科班出身&#xff0c;就职于医疗科技公司&#xff0c;热衷分享知识&#xf…

python学习(不是广告)是我自己看了这么多课总结的经验

入门 首先推荐的是林粒粒的python入门学习 在你看完这套Python入门教程后&#xff1a; 想继续巩固基础 &#x1f449; 想学习Python数据分析 &#x1f449; 想学习Python AI大模型应用开发 &#x1f449; 进阶 入门之后就是进阶使用python实现 1.办公效率化 2.数据分析&am…

信弘智能与图为科技共探科技合作新蓝图

本期导读 近日&#xff0c;图为信息科技&#xff08;深圳&#xff09;有限公司迎来上海信弘智能科技有限公司代表的到访&#xff0c;双方共同探讨英伟达生态系统在人工智能领域的发展。 在科技日新月异的今天&#xff0c;跨界合作与技术交流成为了推动行业发展的重要驱动。7月…

GraphRAG+ollama+LM Studio+chainlit

这里我们进一步尝试将embedding模型也换为本地的&#xff0c;同时熟悉一下流程和学一些新的东西 1.环境还是用之前的&#xff0c;这里我们先下载LLM 然后你会在下载nomic模型的时候崩溃&#xff0c;因为无法搜索&#xff0c;无法下载 解决办法如下lm studio 0.2.24国内下载…

Ubuntu 24.04 LTS Noble安装Docker Desktop简单教程

Docker 为用户提供了在 Ubuntu Linux 上快速创建虚拟容器的能力。但是&#xff0c;那些不想使用命令行管理容器的人可以在 Ubuntu 24.04 LTS 上安装 Docker Desktop GUI&#xff0c;本教程将提供用于设置 Docker 图形用户界面的命令…… Docker Desktop 是一个易于使用的集成容…

脑肿瘤有哪些分类? 哪些人会得脑肿瘤?

脑肿瘤&#xff0c;作为一类严重的脑部疾病&#xff0c;其分类复杂多样&#xff0c;主要分为原发性脑肿瘤和脑转移瘤两大类。原发性脑肿瘤起源于颅内组织&#xff0c;常见的有胶质瘤、脑膜瘤、生殖细胞瘤、颅内表皮样囊肿及鞍区肿瘤等。其中&#xff0c;胶质瘤作为最常见的脑神…

nodejs学习之process.env.NODE_ENV

简介 process对象是 Node 的一个全局对象&#xff0c;提供当前 Node 进程的信息。它可以在脚本的任意位置使用&#xff0c;不必通过require命令加载。该对象部署了EventEmitter接口。 process.env 属性返回包含用户环境的对象 使用 pnpm init新建index.js const { env } r…

【C++】类和对象(二)

个人主页 创作不易&#xff0c;感谢大家的关注&#xff01; 文章目录 ⭐一、类的默认成员函数&#x1f48e;二、构造函数⏱️三、析构函数&#x1f3dd;️ 四、拷贝构造函数&#x1f384;五、赋值运算符重载&#x1f3e0;六、取地址运算符重载&#x1f389;const成员 ⭐一、类…

系统架构设计师教程 第3章 信息系统基础知识-3.7 企业资源规划(ERP)-解读

系统架构设计师教程 第3章 信息系统基础知识-3.7 企业资源规划&#xff08;ERP&#xff09; 3.7.1 企业资源规划的概念3.7.2 企业资源规划的结构3.7.2.1 生产预测3.7.2.2 销售管理&#xff08;计划&#xff09;3.7.2.3 经营计划&#xff08;生产计划大纲&#xff09;3.7.2.4 …