【Vue3源码学习】— CH2.5 reactiveEffect.ts:Vue 3响应式系统的核心
reactiveEffect.ts:Vue 3响应式系统的核心
- 1. 什么是 reactiveEffect?
- 2. 核心机制
- 2.1 依赖收集(Track)
- 2.2 触发更新(Trigger)
- 2.3 效果范围(effectScope)
- 3. 源码解析 —— track
- 3.1 track
- 3.2 参数 target 究竟是什么
- 3.2.1 target是什么
- 3.2.2 怎么理解target
- 3.2.3 小结
- 4. 源码解析 —— trigger
- 5. 应用场景
- 5.1 组件渲染
- 5.2 计算属性
- 5.3 观察者(watch)
- 6. 小结
Vue 3的响应式系统是基于Proxy和Reflect API构建的,其中reactiveEffect扮演了核心角色,实现了数据的响应式变化追踪和视图的自动更新。本章节我们将深入探讨reactiveEffect的工作原理。
1. 什么是 reactiveEffect?
在Vue中,每当我们操作响应式数据时,都会触发reactiveEffect来跟踪这些操作。无论是计算属性、watch监听器,还是组件的渲染函数,都被视为副作用函数,并被reactiveEffect所管理。
2. 核心机制
2.1 依赖收集(Track)
track函数负责在副作用函数首次执行时收集所有被访问的响应式数据的依赖。这意味着,当一个数据项被读取时,所有依赖于这个数据的副作用函数都会被记录下来。
2.2 触发更新(Trigger)
当响应式数据变化时,trigger函数会找到所有依赖于这个数据的副作用函数,并重新执行它们,从而实现数据到视图的自动更新。
2.3 效果范围(effectScope)
effectScope是Vue 3引入的新概念,它允许开发者组织和管理多个reactiveEffect。这对于在组件卸载或需要清理副作用时非常有用,避免内存泄漏。
3. 源码解析 —— track
3.1 track
/*** track函数负责追踪一个响应式对象的属性访问操作** 它的主要作用是确定当前哪个副作用函数(effect)正在运行* 并将这个副作用函数记录为该响应式属性的依赖** @param target - 被访问属性所属的响应式对象.* @param type - 访问类型,由TrackOpTypes枚举定义,比如读取、写入.* @param key - 被访问的响应式属性的标识符.*/
export function track(target: object, type: TrackOpTypes, key: unknown) {/*** 1.条件判断: 首先检查是否应该进行依赖收集,这由shouldTrack和activeEffect共同决定。* shouldTrack是一个标志,表明是否应该收集依赖;* activeEffect指的是当前正在执行的副作用函数。*/if (shouldTrack && activeEffect) {/*** 2.获取依赖映射: 使用targetMap(一个全局WeakMap,在上一章节详细介绍过)尝试获取当前对象的依赖映射depsMap。* 如果不存在,就为这个对象创建一个新的Map并设置到targetMap中。*/let depsMap = targetMap.get(target)if (!depsMap) {targetMap.set(target, (depsMap = new Map()))}/*** 3. 获取依赖集合: 尝试从depsMap中获取对应属性key的依赖集合dep。* 如果这个属性还没有依赖集合,就创建一个新的依赖集合,并设置一个清理函数,当依赖集合为空时从depsMap中移除这个属性的记录*/let dep = depsMap.get(key)if (!dep) {depsMap.set(key, (dep = createDep(() => depsMap!.delete(key))))}/*** 4. 追踪效果: 最后,使用trackEffect函数将当前的activeEffect(副作用函数)添加到这个属性的依赖集合中。* 如果是开发模式(__DEV__为true),还会传递额外的调试信息,包括被访问的对象、访问类型和属性标识符。*/trackEffect(activeEffect,dep,__DEV__? {target,type,key,}: void 0,)}
}
3.2 参数 target 究竟是什么
在 baseHandlers.ts 里的get函数里,我们说target是原始对象,而在track函数里,我们说target是被访问属性所属的响应式对象,但是它们又是同一个值,这让人很困惑。
3.2.1 target是什么
看一下proxy里关于捕获器的实例:
const target ={foo: 'bar'
}
const handler={get(trapTarget, property, receiver) {console.log(trapTarget === target);console.log(property);console.log(receiver === proxy); }
}
const proxy = new Proxy(target, handler);
proxy.foo;//输出
//true
//foo
//true
所以在技术上无论是get函数里,还是track函数里,target是原始对象。
3.2.2 怎么理解target
看一下proxy最基础的示例:
const target={id:'target'
};
const handle={};
const proxy = new Proxy(target,handle);// id 属性会访问同一个值
console.log(target.id); // target
console.log(proxy.id); // target// 给目标(target)属性赋值会反映在两个对象上 ,因为两个对象访问的是同一个值
target.id = 'foo';
console.log(target.id); // foo
console.log(proxy.id); // foo
3.2.3 小结
简而言之,尽管track的参数是原始对象,但在Vue的响应式系统上下文中,我们可以将其等同于它的响应式代理,因为所有操作实际上都是针对这个代理执行的。
4. 源码解析 —— trigger
/*** trigger函数负责找到所有依赖于这些数据的副作用函数,* 并执行它们以更新视图或执行其他副作用** @param target - 发生变化的响应式对象.* @param type - 变化的类型,由TriggerOpTypes枚举定义,如设置(SET)、添加(ADD)、删除(DELETE)等.* @param key - 发生变化的具体属性名.*/
export function trigger(target: object,type: TriggerOpTypes,key?: unknown,newValue?: unknown, //新值(对于SET操作)oldValue?: unknown, //旧值(对于SET操作)oldTarget?: Map<unknown, unknown> | Set<unknown>, //旧的集合对象(对于集合类型,如Map、Set的变化)
) {/*** 寻找依赖:如果depsMap不存在,意味着target从未被追踪过依赖,直接返回*/const depsMap = targetMap.get(target)if (!depsMap) {// never been trackedreturn}/*** 确定需要触发的依赖集合(deps):根据变化的类型(type)和具体的属性(key),确定需要触发的依赖集合* 特殊情况处理:如整个集合被清除(CLEAR)、数组长度变化、Map集合的特定操作等,都有针对性的逻辑来确定哪些依赖需要被触发*/let deps: (Dep | undefined)[] = []if (type === TriggerOpTypes.CLEAR) {// collection being cleared// trigger all effects for targetdeps = [...depsMap.values()]} else if (key === 'length' && isArray(target)) {const newLength = Number(newValue)depsMap.forEach((dep, key) => {if (key === 'length' || (!isSymbol(key) && key >= newLength)) {deps.push(dep)}})} else {// schedule runs for SET | ADD | DELETEif (key !== void 0) {deps.push(depsMap.get(key))}// also run for iteration key on ADD | DELETE | Map.SETswitch (type) {case TriggerOpTypes.ADD:if (!isArray(target)) {deps.push(depsMap.get(ITERATE_KEY))if (isMap(target)) {deps.push(depsMap.get(MAP_KEY_ITERATE_KEY))}} else if (isIntegerKey(key)) {// new index added to array -> length changesdeps.push(depsMap.get('length'))}breakcase TriggerOpTypes.DELETE:if (!isArray(target)) {deps.push(depsMap.get(ITERATE_KEY))if (isMap(target)) {deps.push(depsMap.get(MAP_KEY_ITERATE_KEY))}}breakcase TriggerOpTypes.SET:if (isMap(target)) {deps.push(depsMap.get(ITERATE_KEY))}break}}/*** 触发依赖更新:* 遍历所有需要触发的依赖集合deps,对于每个依赖(即副作用函数集合),调用triggerEffects函数来实际执行它们。* 在执行依赖前,调用pauseScheduling暂停调度(防止在依赖执行过程中产生无限循环调用),执行完后调用resetScheduling恢复调度。*/pauseScheduling()for (const dep of deps) {if (dep) {triggerEffects(dep,DirtyLevels.Dirty,__DEV__? {target,type,key,newValue,oldValue,oldTarget,}: void 0,)}}resetScheduling()
}
5. 应用场景
5.1 组件渲染
Vue 组件的渲染过程本身是一个副作用。Vue 使用reactiveEffect来自动重新渲染组件,当组件依赖的响应式数据变化时。
5.2 计算属性
计算属性是基于其它响应式数据计算得出的值。Vue内部使用reactiveEffect来跟踪计算属性依赖的数据,确保它们在依赖数据变化时更新。
5.3 观察者(watch)
Vue的watchAPI允许你观察响应式数据的变化,并在变化时执行回调函数。这背后也是使用reactiveEffect实现的。
6. 小结
通过本章的学习,我们了解了reactiveEffect在Vue 3响应式系统中的核心作用:依赖收集与更新触发。
而这里涉及的一些关键逻辑,如:shouldTrack、activeEffect等,我们会在下一个章节继续探讨。
相关文章:

【Vue3源码学习】— CH2.5 reactiveEffect.ts:Vue 3响应式系统的核心
reactiveEffect.ts:Vue 3响应式系统的核心 1. 什么是 reactiveEffect?2. 核心机制2.1 依赖收集(Track)2.2 触发更新(Trigger)2.3 效果范围(effectScope) 3. 源码解析 —— track3.1 …...
K8S的mountPath和subPath
1 mountPath mountPath是容器内部文件系统的挂载点,它定义了容器内部将外部存储卷(如 PersistentVolume、ConfigMap、Secret 等)挂载到哪个路径下。通过 mountPath,容器可以访问这些挂载的数据或配置。 2 subPath subPath 是 m…...

notepad++里安装32位和64位的16进制编辑器Hex-Editor
这个16进制编辑器确实是个好东西,平时工作种会经常用到, 这是hex-editor的官网。这个里边只能下载32位的(64位的看最下边),选一个合适的版本,我当时选的是最新的版本 https://sourceforge.net/projects/npp-plugins/files/Hex%20E…...
Python类的基本结构
当我们在Python中定义类时,我们实际上是在创建一种新的数据类型。类允许我们定义对象的属性和方法,从而构建更复杂的程序。让我们深入探讨一下关于类的一些重要概念。 定义类:基本结构 一个类的基本结构包括以下部分: 类名&…...

利用HIVE的窗口函数进行SQL查询中出现的问题记录
student_info部分数据 score_info部分数据 course_info 1、问题复现 --完整SQL selectsti.stu_id,sti.stu_name,concat_ws(",",collect_set(ci.course_name)) over(partition by sti.stu_id) fromstudent_info sti left joinscore_info sci onsti.stu_idsci.stu_id l…...

更改chatglm认知
ChatGLM-Efficient-Tuning 下载源代码 下载ChatGLM-Efficient-Tuning 解压 创建虚拟环境 conda create --prefixD:\CondaEnvs\chatglm6btrain python3.10 cd D:\ChatGLM-Efficient-Tuning-main conda activate D:\CondaEnvs\chatglm6btrain安装所需要的包 pip install -r…...
WPF 界面命令绑定(MVVM结构)
1.创建模型数据类(M) /// <summary>/// 数据模型/// </summary>public class LoginDataModel{// 用户名private string _userName;public string UserName{get { return _userName; }set{_userName value;}}// 密码private string _passWor…...

常见手撕项目C++
常见手撕项目C 设计模式单例模式饿汉模式懒汉模式 策略模式策略接口实现具体的策略(虚函数重写)定义上下文用户调用 设计模式 单例模式 单例模式是一种常用的软件设计模式,其目的是确保一个类只有一个实例,并提供一个全局访问点来…...
创建一个批处理作业来处理大量数据,例如从数据库中读取数据并进行处理
创建一个批处理作业来处理大量数据,例如从数据库中读取数据并进行处理 要创建一个批处理作业来处理大量数据,您可以使用Spring Batch。Spring Batch是一个用于大规模批处理的框架,它提供了丰富的功能来处理复杂的批处理任务,如读…...
LeetCode 2.两数相加
给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。 请你将两个数相加,并以相同形式返回一个表示和的链表。 你可以假设除了数字 0 之外,这两个数都不会以 0 …...
如何利用ChatGPT提升学术论文写作效率
ChatGPT无限次数:点击直达 如何利用ChatGPT提升学术论文写作效率 ChatGPT 是一种基于大规模预训练模型的自然语言处理工具,可以在各种文本生成任务中发挥作用,包括学术论文写作。利用ChatGPT,可以提高学术论文写作的速度和质量,帮…...

LLMs之Mistral:Mistral 7B v0.2的简介、安装和使用方法、案例应用之详细攻略
LLMs之Mistral:Mistral 7B v0.2的简介、安装和使用方法、案例应用之详细攻略 导读:Mistral AI首个7B模型发布于2023年9月,在基准测试中超越Llama 2 13B,一下子声名大振。Mistral 7B v0.2对应的指令调优版本Mistral-7B-Instruct-v0…...
深入解析Oracle数据库中的WITH AS(CTE)原理
Oracle数据库中的WITH AS子句(也称为公用表表达式CTE(Common Table Expression))是一种高级查询构造工具,它允许在一条SQL语句的开始部分定义临时的结果集(或称子查询),这个结果集可以被随后的查询主体多次…...

Linux 环境安装 Elasticsearch 8.X
安装前说明 首先确定操作系统,在Linux发行版上执行uname -a查看具体系统。我是Ubuntu系统,可以用直接用apt-get安装,也可以下载tar.gz包手动安装。使用apt-get安装更方便快速,但不同的文件会被安装到不同的目录,不方便…...
Java零基础-集合:函数式接口
哈喽,各位小伙伴们,你们好呀,我是喵手。 今天我要给大家分享一些自己日常学习到的一些知识点,并以文字的形式跟大家一起交流,互相学习,一个人虽可以走的更快,但一群人可以走的更远。 我是一名后…...
Redis Scan指令解析与使用示例
Redis Scan指令解析与使用示例 概念 想要从redis key列表中找到某个key,redis提供了一个简单粗暴的指令keys用来列出满足查询条件的所有key。 keys redis* keys redis*keykey指令非常简单,只要提供一个简单的正则表达式即可,但是有两个明显的…...

Qt+OpenGL入门教程(三)——绘制三角形
通过前两篇文章的学习,我想大家应该有了基本的理解,我们接下来实操一下。 创建Qt OpenGL窗口 QOpenGLWidget QGLWidget是传统QtOpenGL模块的一部分,与其他QGL类一样,应该在新的应用程序中避免使用。相反,从Qt5.4开始…...

springcloud基本使用(搭建eureka服务端)
创建springbootmaven项目 next next finish创建成功 删除项目下所有文件目录,只保留pox.xml文件 父项目中的依赖: springboot依赖: <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-s…...

第十二章:预处理命令
文章目录 第十二章:预处理命令宏定义无参宏定义带参数的宏定义 文件包含处理 第十二章:预处理命令 作用:由编译预处理程序对程序中的特殊命令作出解释,以产生新的源程序对其进行正式编译 C语言与其他语言的重要区别就是可以使用预…...

Game Audio Programming
音频编程时游戏开发中最容易忽略,学习资源又是很少的环节。接下来,你将和我探索人耳的工作机制。 what is sound? 我们可以解释电视机是如何通过眼睛传递视觉信息的,但却往往无法对听觉信息做出类似的解释。 对声音的科学研究被称为声学&…...
【AI学习】三、AI算法中的向量
在人工智能(AI)算法中,向量(Vector)是一种将现实世界中的数据(如图像、文本、音频等)转化为计算机可处理的数值型特征表示的工具。它是连接人类认知(如语义、视觉特征)与…...

Netty从入门到进阶(二)
二、Netty入门 1. 概述 1.1 Netty是什么 Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients. Netty是一个异步的、基于事件驱动的网络应用框架,用于…...

【JVM】Java虚拟机(二)——垃圾回收
目录 一、如何判断对象可以回收 (一)引用计数法 (二)可达性分析算法 二、垃圾回收算法 (一)标记清除 (二)标记整理 (三)复制 (四ÿ…...

Appium下载安装配置保姆教程(图文详解)
目录 一、Appium软件介绍 1.特点 2.工作原理 3.应用场景 二、环境准备 安装 Node.js 安装 Appium 安装 JDK 安装 Android SDK 安装Python及依赖包 三、安装教程 1.Node.js安装 1.1.下载Node 1.2.安装程序 1.3.配置npm仓储和缓存 1.4. 配置环境 1.5.测试Node.j…...
虚幻基础:角色旋转
能帮到你的话,就给个赞吧 😘 文章目录 移动组件使用控制器所需旋转:组件 使用 控制器旋转将旋转朝向运动:组件 使用 移动方向旋转 控制器旋转和移动旋转 缺点移动旋转:必须移动才能旋转,不移动不旋转控制器…...

表单设计器拖拽对象时添加属性
背景:因为项目需要。自写设计器。遇到的坑在此记录 使用的拖拽组件时vuedraggable。下面放上局部示例截图。 坑1。draggable标签在拖拽时可以获取到被拖拽的对象属性定义 要使用 :clone, 而不是clone。我想应该是因为draggable标签比较特。另外在使用**:clone时要将…...
在Spring Boot中集成RabbitMQ的完整指南
前言 在现代微服务架构中,消息队列(Message Queue)是实现异步通信、解耦系统组件的重要工具。RabbitMQ 是一个流行的消息中间件,支持多种消息协议,具有高可靠性和可扩展性。 本博客将详细介绍如何在 Spring Boot 项目…...
java+webstock
maven依赖 <dependency><groupId>org.java-websocket</groupId><artifactId>Java-WebSocket</artifactId><version>1.3.5</version></dependency><dependency><groupId>org.apache.tomcat.websocket</groupId&…...

可视化预警系统:如何实现生产风险的实时监控?
在生产环境中,风险无处不在,而传统的监控方式往往只能事后补救,难以做到提前预警。但如今,可视化预警系统正在改变这一切!它能够实时收集和分析生产数据,通过直观的图表和警报,让管理者第一时间…...
npm install 相关命令
npm install 相关命令 基本安装命令 # 安装 package.json 中列出的所有依赖 npm install npm i # 简写形式# 安装特定包 npm install <package-name># 安装特定版本 npm install <package-name><version>依赖类型选项 # 安装为生产依赖(默认&…...