【Vue3源码】第一章 effect和reactive
文章目录
- 【Vue3源码】第一章 effect和reactive
- 前言
- 1、实现effect函数
- 2、封装track函数(依赖收集)
- 3、封装reactive函数
- 4、封装trigger函数(依赖触发)
- 5、单元测试
【Vue3源码】第一章 effect和reactive
前言
今天就正式开始Vue3源码学习了,那么很多初学者(包括我)在看vue源码时都会非常迷茫不知从何下手,所以我们在学习源码时应该反其道而行之,剔除掉源码中包括Tree-Shaking、TypeScript类型约束、特性开关、错误处理等操作,只了解其核心原理,大大提高学习效率减少学习成本!
如果你还不了解Vue源码设计“Tree-Shaking、TypeScript类型约束、特性开关、错误处理”?
可以购买一本《Vue.js设计与实现》,在第 2 章 “框架设计的核心要素” 有详细介绍了这些操作,并且有举例子解释了为什么要这么设计。
好了!正式开始学习,想要了解vue3的源码,我们可以先从reactivity文件夹入手。
那么reactivity文件夹是什么呢??
Reactivity:里面写的是vue最最重要的功能那就是“vue被人津津乐道并且顶顶大名的响应式源码“。
今天我们就简单的从零开始实现一遍effect函数(影响,效果),reactive函数(代理或者劫持)。
在reactive函数中还包括两个功能依赖收集和依赖触发,他们分别是track函数(依赖收集)和trigger函数(依赖触发),在今天的文章中我都会一一实现一遍。
1、实现effect函数
effect可以说是响应式系统的核心,所以我们学习vue3源码时推荐从effect入手。
如果你还没有搭建好jest单元测试环境,可以查看我的上一篇文章
- 首先我们新建一个effect.ts文件并且封装一个effect函数
//用来暂存effect传递的参数fn
let activeEffect;export const effect = (fn) => {activeEffect = fnfn()
};
为什么我们还要在函数定义一个activeEffect
块级作用域变量呢?
因为activeEffect
要帮我们暂存用户传进来的fn参数,fn的类型是一个函数,那么fn暂存到了activeEffect
变量后为了不让下次传递的参数覆盖掉了我们的之前的fn,Vue就该把它存到一个“仓库”里保存着,方便后续管理,这个过程叫依赖收集。
我们再优化一下代码方便后续管理。
class ReactiveEffect {private _fn;constructor(fn) {this._fn = fn;}run() {activeEffect = this;this._fn();}
}export const effect = (fn) => {const _effect = new ReactiveEffect(fn);_effect.run();};
那么怎么才能做到依赖收集呢?往下看⬇️
2、封装track函数(依赖收集)
在effect.ts文件中封装track函数
为了方便理解,我把targetMap
抽象的比做一个仓库方便大家理解,作为一个仓库,自然就得有仓库的管理规则,货物进来总不能不分类就乱七八糟直接存进去吧?
所以我们就根据传递进来的参数,target(一个对象),key(该对象中的key)作为仓库的类别,一一细化分类这个仓库里包含的货物。
//targetMap就是一个收集effect传递过来的参数fn的“总仓库”
const targetMap = new WeakMap();export function track(target, key) {//根据target一级分类let depsMap = targetMap.get(target);//如果这个货物是第一次存,我们就新建这个分类if (!depsMap) {depsMap = new Map();targetMap.set(target, depsMap);}//根据key二级分类let dep = depsMap.get(key);//如果这个货物是第一次存,我们就新建这个二级分类if (!dep) {dep = new Set();depsMap.set(key, dep);}//最后把收集到的activeEffect存入这个细化的分类里dep.add(activeEffect);
}
所以targetMap
作为一个总仓库,它通过传入的target和key进行分类保存我们的activeEffect。
我画一个drawio图,看一下就可以明白里面的逻辑。
那么我们的track函数接收的target和key参数又是怎么来的???往下看⬇️
3、封装reactive函数
我们都知道,vue3使用的是Proxy
代理对象实现了数据响应式,Proxy(MDN对Proxy的介绍)代理多达13种捕获器它们可以完美的监听到任何方式的数据改变,完美的解决了的vue2使用Object.defineProperty
时监听不到数组下标缺点。
不过说到缺点:真是非常的尴尬。。。不管Vue2的劫持还是Vue3的代理对有深层嵌套的对象还是要用递归去处理并且返回多层的响应式对象。
我们今天的文章只处理没有嵌套关系的对象,不深入多层嵌套对象的递归处理,下次再处理这个逻辑。
极简版reactive函数很简单,return反射的结果之前进行依赖收集即可,传入的target正是代理对象,key是正在使用对象的key。
新建一个reactive文件
import { track, trigger } from "./effect"export const reactive = (raw) => {return new Proxy(raw,{get(target,key,receiver) {const res = Reflect.get(target,key,receiver)//do something 收集依赖track(target,key)return res},set(target,key,value,receiver) {const res = Reflect.set(target,key,value,receiver)// do something 触发依赖trigger(target,key)return res},})
}
4、封装trigger函数(依赖触发)
上一步封装好了reavtive函数的还差最后一个trigger函数我们还没有封装。
其实依赖触发也很简单,通过传入的target和key(货物的分类类别),我们就可以从总仓库里取出收集到的对应依赖,并且再出触发它!
在effect.ts文件中封装trigger
export function trigger(target, key) {let depsMap = targetMap.get(target);let dep = depsMap.get(key);for (let effect of dep) {effect.run();}
}
5、单元测试
我们新建在test文件夹下新建一个 effect.spec.ts文件
然后就可以打上断点跟着断点走一遍Vue响应式的执行帮助我们理解Vue的逻辑。
我测试的代码如下:
import { effect } from "../effect";
import { reactive } from "../reactive";describe("effect", () => {it("happy path", () => {const user = reactive({age: 10,name:'www',newObj:{objAge:11}});let nextAge;let age2effect(() => {nextAge =user.age + 1;});//无法代理深层嵌套的对象effect(() => {age2 = user.newObj.objAge;});expect(nextAge).toBe(11);user.age++;expect(nextAge).toBe(12);user.age = 99expect(nextAge).toBe(100)expect(age2).toBe(11)//对于深层嵌套的对象由于没有封装递归的逻辑所以监听不到user.newObj.objAge ++//理论上来说age2应该跟着user.newobj.objeAge响应式变成12,而结果却没有变化expect(age2).toBe(11)});
});
相关文章:

【Vue3源码】第一章 effect和reactive
文章目录【Vue3源码】第一章 effect和reactive前言1、实现effect函数2、封装track函数(依赖收集)3、封装reactive函数4、封装trigger函数(依赖触发)5、单元测试【Vue3源码】第一章 effect和reactive 前言 今天就正式开始Vue3源码…...
C函数指针
函数指针是指向函数的指针变量。通常我们说的指针变量是指向一个整型、数组或字符型等变量,而函数指针是指向函数。函数指针可以像一般函数一样,用于调用函数、传递参数。函数指针变量的声明:typedef int (*fun_ptr)(int,int); // 声明一个指…...

2023同等学力申请硕士计算机综合国考
同等学力国考报名要开始了 2023年2月15日,中国教育考试网和“全国同等学力人员申请硕士学位管理工作信息平台”(https://tdxl.chsi.com.cn,联系服务电话:010-67410388)公布报名工作通知。考生须按照通知要求进行注册或…...
英语基础-并列句概述
什么是并列句?并列句就是用连词把独立的句子连接起来,使得句子之间产生并列的逻辑。 1. 并列句中的逻辑 1. 小明步行上学,小红骑自行车上班。 Ming goes to school on foot,and Hong goes to work by bike. 平行逻辑 2. 小红经常玩手机…...

大数据框架之Hadoop:HDFS(一)HDFS概述
1.1HDFS产出背景及定义 HDFS 产生背景 随着数据量越来越大,在一个操作系统存不下所有的数据,那么就分配到更多的操作系统管理的磁盘中,但是不方便管理和维护,迫切需要一种系统来管理多台机器上的文件,这就是分布式文件…...

20230210组会论文总结
目录 【Ultra-High-Definition Low-Light Image Enhancement: A Benchmark and Transformer-Based Method】 【ShuffleMixer: An Efficient ConvNet for Image Super-Resolution】 【A Close Look at Spatial Modeling: From Attention to Convolution 】 【DEA-Net: Single i…...

Python - 数据容器dict(字典)
目录 字典的定义 字典数据的获取 字典的嵌套 字典的各种操作 新增与更新元素 [Key] Value 删除元素 pop和del 清空字典 clear 获取全部的键 keys 遍历字典 容器通用功能总览 字典的定义 使用{},不过存储的元素是一个个的:键值对&#…...
傻白探索Chiplet,文献阅读笔记汇总(十二)
Summary(方便分类管理) Article(文献出处) 方便再次搜索 Data(文献数据) 总结归纳,方便理解 Comments(对文献的想法)/Why(为什么看这篇文献)强…...

#电子电气架构——Vector工具常见问题解决三板斧
我是穿拖鞋的汉子,魔都中一位坚持长期主义的工科男。 今天在与母亲聊天时,得到老家隔壁邻居一位大姐年初去世的消息,挺让自己感到伤感!岁月如流水,想抓都抓不住。想起平时自己加班的那个程度,可能后续也要自己注意身体啦。 老规矩,分享一段喜欢的文字,避免自己成为高知…...
文本三剑客之grep
Grep是Linux用户用来搜索文本字符串的命令行工具。您可以使用它在文件中搜索某个单词或单词的组合,也可以将其他Linux命令的输出通过管道传输到grep,因此grep可以仅显示您需要查看的输出。grep的命令格式如下:grep 选项 查找条件 目标文件…...

pwn手记录题1
fuzzerinstrospector(首届数字空间安全攻防大赛) 主体流程(相对比较简单,GLibc为常见的2.27版本, Allocate申请函数(其中有两个输入函数Read_8Int、Read_context; 还存在着后门函数; 关键点在于如何利用…...

自动驾驶规划 - Apollo Lattice Planner算法【1】
文章目录Lattice Planner简介Lattice Planner 算法思路1. 离散化参考线的点2. 在参考线上计算匹配点3. 根据匹配点,计算Frenet坐标系的S-L值4. parse the decision and get the planning target5. 生成横纵向采样路径6. 轨迹cost值计算,进行碰撞检测7. 优…...

以太坊数据开发-Web3.py-安装连接以太坊数据
Web3.py是连接以太坊的python库,它的API从web3.js中派生而来。如果你用过web3.js,你会对它的API很熟悉。但惭愧的是,作为一个以太坊上Dapp的开发者,我几乎没有直接使用过web3.js,也没有看过它的API。 官网:…...

【触摸屏功能测试】MQTT_STD本地调试说明-测试记录
1、MQTT简介 MQTT是一种基于发布/订阅模式的“轻量级”通讯协议。它是针对受限的、低带宽的、高延迟的、网络不可靠的环境下的网络通讯设备设计的。 发布是指客户端将消息传递给服务器,订阅是指客户端接收服务器推送的消息。每个消息有一个主题,包含若干…...

六十分之十三——黎明前
目录一、目标二、计划三、完成情况四、提升改进(最少3点)五、意外之喜(最少2点)六、总结一、目标 明确可落地,对于自身执行完成需要一定的努力才可以完成的 1.8本技术管理书籍阅读(使用番茄、快速阅读、最后输出思维导图)2.吴军系列硅谷来信1听书、香帅的北大金融…...

【Call for papers】CRYPTO-2023(CCF-A/网络与信息安全/2023年2月16日截稿)
Crypto 2023 will take place in Santa Barbara, USA on August 19-24, 2023. Crypto 2023 is organized by the International Association for Cryptologic Research (IACR). The proceedings will be published by Springer in the LNCS series. 文章目录1.会议信息2.时间节…...
线程的信号量和互斥量
文章目录线程的信号量初始化信号量:sem_init减少信号量:sem_wait增加信号量:sem_post删除信号量:sem_destroy代码示例线程的互斥量初始化互斥量:pthread_mutex_init锁住互斥量:pthread_mutex_lock解锁互斥量…...
关于Linux,开源社区与国产化的本质区别
因为生产力驱动而非理想主义驱动。 开源运动的蓬勃发展来自于GNU(GNU is not unix),RichardMatthewStallman领导着一群黑客,带着对比尔盖茨的鄙视,制定了GPL协议,以后人人都能从伟大的前人身上学习到源代码的精髓,让软…...

Win11下Linux子系统迁移方法及报错解决
Win11 将Linux子系统从C盘迁移到其他盘Win11下Linux子系统迁移方法及报错解决1、下载LxRunOffline2、ERROR:directory is not empty 报错解决参考链接Win11下Linux子系统迁移方法及报错解决 C盘满了,Ubuntu子系统占了100多G怎么办?直接将子系…...
python维护的一些基础方法
1】通过命令行查看python安装库的基本信息 pip show numpy # 查看python中numpy库的安装版本信息 2】python 环境的开发与维护 python的开发与C\MATLAB等最大的不同就是,python中版本的更新不对历史版本负责,就是说你以历史版本开发的python程序&#…...

树莓派超全系列教程文档--(61)树莓派摄像头高级使用方法
树莓派摄像头高级使用方法 配置通过调谐文件来调整相机行为 使用多个摄像头安装 libcam 和 rpicam-apps依赖关系开发包 文章来源: http://raspberry.dns8844.cn/documentation 原文网址 配置 大多数用例自动工作,无需更改相机配置。但是,一…...

HTML 列表、表格、表单
1 列表标签 作用:布局内容排列整齐的区域 列表分类:无序列表、有序列表、定义列表。 例如: 1.1 无序列表 标签:ul 嵌套 li,ul是无序列表,li是列表条目。 注意事项: ul 标签里面只能包裹 li…...
c++ 面试题(1)-----深度优先搜索(DFS)实现
操作系统:ubuntu22.04 IDE:Visual Studio Code 编程语言:C11 题目描述 地上有一个 m 行 n 列的方格,从坐标 [0,0] 起始。一个机器人可以从某一格移动到上下左右四个格子,但不能进入行坐标和列坐标的数位之和大于 k 的格子。 例…...
Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理
引言 Bitmap(位图)是Android应用内存占用的“头号杀手”。一张1080P(1920x1080)的图片以ARGB_8888格式加载时,内存占用高达8MB(192010804字节)。据统计,超过60%的应用OOM崩溃与Bitm…...
#Uniapp篇:chrome调试unapp适配
chrome调试设备----使用Android模拟机开发调试移动端页面 Chrome://inspect/#devices MuMu模拟器Edge浏览器:Android原生APP嵌入的H5页面元素定位 chrome://inspect/#devices uniapp单位适配 根路径下 postcss.config.js 需要装这些插件 “postcss”: “^8.5.…...

基于Java+MySQL实现(GUI)客户管理系统
客户资料管理系统的设计与实现 第一章 需求分析 1.1 需求总体介绍 本项目为了方便维护客户信息为了方便维护客户信息,对客户进行统一管理,可以把所有客户信息录入系统,进行维护和统计功能。可通过文件的方式保存相关录入数据,对…...
Go 并发编程基础:通道(Channel)的使用
在 Go 中,Channel 是 Goroutine 之间通信的核心机制。它提供了一个线程安全的通信方式,用于在多个 Goroutine 之间传递数据,从而实现高效的并发编程。 本章将介绍 Channel 的基本概念、用法、缓冲、关闭机制以及 select 的使用。 一、Channel…...
BLEU评分:机器翻译质量评估的黄金标准
BLEU评分:机器翻译质量评估的黄金标准 1. 引言 在自然语言处理(NLP)领域,衡量一个机器翻译模型的性能至关重要。BLEU (Bilingual Evaluation Understudy) 作为一种自动化评估指标,自2002年由IBM的Kishore Papineni等人提出以来,…...

Neko虚拟浏览器远程协作方案:Docker+内网穿透技术部署实践
前言:本文将向开发者介绍一款创新性协作工具——Neko虚拟浏览器。在数字化协作场景中,跨地域的团队常需面对实时共享屏幕、协同编辑文档等需求。通过本指南,你将掌握在Ubuntu系统中使用容器化技术部署该工具的具体方案,并结合内网…...

边缘计算网关提升水产养殖尾水处理的远程运维效率
一、项目背景 随着水产养殖行业的快速发展,养殖尾水的处理成为了一个亟待解决的环保问题。传统的尾水处理方式不仅效率低下,而且难以实现精准监控和管理。为了提升尾水处理的效果和效率,同时降低人力成本,某大型水产养殖企业决定…...