当前位置: 首页 > news >正文

【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&#xff1a;Vue 3响应式系统的核心 1. 什么是 reactiveEffect&#xff1f;2. 核心机制2.1 依赖收集&#xff08;Track&#xff09;2.2 触发更新&#xff08;Trigger&#xff09;2.3 效果范围&#xff08;effectScope&#xff09; 3. 源码解析 —— track3.1 …...

K8S的mountPath和subPath

1 mountPath mountPath是容器内部文件系统的挂载点&#xff0c;它定义了容器内部将外部存储卷&#xff08;如 PersistentVolume、ConfigMap、Secret 等&#xff09;挂载到哪个路径下。通过 mountPath&#xff0c;容器可以访问这些挂载的数据或配置。 2 subPath subPath 是 m…...

notepad++里安装32位和64位的16进制编辑器Hex-Editor

这个16进制编辑器确实是个好东西&#xff0c;平时工作种会经常用到&#xff0c; 这是hex-editor的官网。这个里边只能下载32位的(64位的看最下边)&#xff0c;选一个合适的版本&#xff0c;我当时选的是最新的版本 https://sourceforge.net/projects/npp-plugins/files/Hex%20E…...

Python类的基本结构

当我们在Python中定义类时&#xff0c;我们实际上是在创建一种新的数据类型。类允许我们定义对象的属性和方法&#xff0c;从而构建更复杂的程序。让我们深入探讨一下关于类的一些重要概念。 定义类&#xff1a;基本结构 一个类的基本结构包括以下部分&#xff1a; 类名&…...

利用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.创建模型数据类&#xff08;M&#xff09; /// <summary>/// 数据模型/// </summary>public class LoginDataModel{// 用户名private string _userName;public string UserName{get { return _userName; }set{_userName value;}}// 密码private string _passWor…...

常见手撕项目C++

常见手撕项目C 设计模式单例模式饿汉模式懒汉模式 策略模式策略接口实现具体的策略&#xff08;虚函数重写&#xff09;定义上下文用户调用 设计模式 单例模式 单例模式是一种常用的软件设计模式&#xff0c;其目的是确保一个类只有一个实例&#xff0c;并提供一个全局访问点来…...

创建一个批处理作业来处理大量数据,例如从数据库中读取数据并进行处理

创建一个批处理作业来处理大量数据&#xff0c;例如从数据库中读取数据并进行处理 要创建一个批处理作业来处理大量数据&#xff0c;您可以使用Spring Batch。Spring Batch是一个用于大规模批处理的框架&#xff0c;它提供了丰富的功能来处理复杂的批处理任务&#xff0c;如读…...

LeetCode 2.两数相加

给你两个 非空 的链表&#xff0c;表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的&#xff0c;并且每个节点只能存储 一位 数字。 请你将两个数相加&#xff0c;并以相同形式返回一个表示和的链表。 你可以假设除了数字 0 之外&#xff0c;这两个数都不会以 0 …...

如何利用ChatGPT提升学术论文写作效率

ChatGPT无限次数:点击直达 如何利用ChatGPT提升学术论文写作效率 ChatGPT 是一种基于大规模预训练模型的自然语言处理工具&#xff0c;可以在各种文本生成任务中发挥作用&#xff0c;包括学术论文写作。利用ChatGPT&#xff0c;可以提高学术论文写作的速度和质量&#xff0c;帮…...

LLMs之Mistral:Mistral 7B v0.2的简介、安装和使用方法、案例应用之详细攻略

LLMs之Mistral&#xff1a;Mistral 7B v0.2的简介、安装和使用方法、案例应用之详细攻略 导读&#xff1a;Mistral AI首个7B模型发布于2023年9月&#xff0c;在基准测试中超越Llama 2 13B&#xff0c;一下子声名大振。Mistral 7B v0.2对应的指令调优版本Mistral-7B-Instruct-v0…...

深入解析Oracle数据库中的WITH AS(CTE)原理

Oracle数据库中的WITH AS子句&#xff08;也称为公用表表达式CTE(Common Table Expression)&#xff09;是一种高级查询构造工具&#xff0c;它允许在一条SQL语句的开始部分定义临时的结果集&#xff08;或称子查询&#xff09;&#xff0c;这个结果集可以被随后的查询主体多次…...

Linux 环境安装 Elasticsearch 8.X

安装前说明 首先确定操作系统&#xff0c;在Linux发行版上执行uname -a查看具体系统。我是Ubuntu系统&#xff0c;可以用直接用apt-get安装&#xff0c;也可以下载tar.gz包手动安装。使用apt-get安装更方便快速&#xff0c;但不同的文件会被安装到不同的目录&#xff0c;不方便…...

Java零基础-集合:函数式接口

哈喽&#xff0c;各位小伙伴们&#xff0c;你们好呀&#xff0c;我是喵手。 今天我要给大家分享一些自己日常学习到的一些知识点&#xff0c;并以文字的形式跟大家一起交流&#xff0c;互相学习&#xff0c;一个人虽可以走的更快&#xff0c;但一群人可以走的更远。 我是一名后…...

Redis Scan指令解析与使用示例

Redis Scan指令解析与使用示例 概念 想要从redis key列表中找到某个key&#xff0c;redis提供了一个简单粗暴的指令keys用来列出满足查询条件的所有key。 keys redis* keys redis*keykey指令非常简单&#xff0c;只要提供一个简单的正则表达式即可&#xff0c;但是有两个明显的…...

Qt+OpenGL入门教程(三)——绘制三角形

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

springcloud基本使用(搭建eureka服务端)

创建springbootmaven项目 next next finish创建成功 删除项目下所有文件目录&#xff0c;只保留pox.xml文件 父项目中的依赖&#xff1a; springboot依赖&#xff1a; <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-s…...

第十二章:预处理命令

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

Game Audio Programming

音频编程时游戏开发中最容易忽略&#xff0c;学习资源又是很少的环节。接下来&#xff0c;你将和我探索人耳的工作机制。 what is sound? 我们可以解释电视机是如何通过眼睛传递视觉信息的&#xff0c;但却往往无法对听觉信息做出类似的解释。 对声音的科学研究被称为声学&…...

镜像里切换为普通用户

如果你登录远程虚拟机默认就是 root 用户&#xff0c;但你不希望用 root 权限运行 ns-3&#xff08;这是对的&#xff0c;ns3 工具会拒绝 root&#xff09;&#xff0c;你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案&#xff1a;创建非 roo…...

【配置 YOLOX 用于按目录分类的图片数据集】

现在的图标点选越来越多&#xff0c;如何一步解决&#xff0c;采用 YOLOX 目标检测模式则可以轻松解决 要在 YOLOX 中使用按目录分类的图片数据集&#xff08;每个目录代表一个类别&#xff0c;目录下是该类别的所有图片&#xff09;&#xff0c;你需要进行以下配置步骤&#x…...

【服务器压力测试】本地PC电脑作为服务器运行时出现卡顿和资源紧张(Windows/Linux)

要让本地PC电脑作为服务器运行时出现卡顿和资源紧张的情况&#xff0c;可以通过以下几种方式模拟或触发&#xff1a; 1. 增加CPU负载 运行大量计算密集型任务&#xff0c;例如&#xff1a; 使用多线程循环执行复杂计算&#xff08;如数学运算、加密解密等&#xff09;。运行图…...

免费数学几何作图web平台

光锐软件免费数学工具&#xff0c;maths,数学制图&#xff0c;数学作图&#xff0c;几何作图&#xff0c;几何&#xff0c;AR开发,AR教育,增强现实,软件公司,XR,MR,VR,虚拟仿真,虚拟现实,混合现实,教育科技产品,职业模拟培训,高保真VR场景,结构互动课件,元宇宙http://xaglare.c…...

Docker拉取MySQL后数据库连接失败的解决方案

在使用Docker部署MySQL时&#xff0c;拉取并启动容器后&#xff0c;有时可能会遇到数据库连接失败的问题。这种问题可能由多种原因导致&#xff0c;包括配置错误、网络设置问题、权限问题等。本文将分析可能的原因&#xff0c;并提供解决方案。 一、确认MySQL容器的运行状态 …...

用神经网络读懂你的“心情”:揭秘情绪识别系统背后的AI魔法

用神经网络读懂你的“心情”:揭秘情绪识别系统背后的AI魔法 大家好,我是Echo_Wish。最近刷短视频、看直播,有没有发现,越来越多的应用都开始“懂你”了——它们能感知你的情绪,推荐更合适的内容,甚至帮客服识别用户情绪,提升服务体验。这背后,神经网络在悄悄发力,撑起…...

比较数据迁移后MySQL数据库和ClickHouse数据仓库中的表

设计一个MySQL数据库和Clickhouse数据仓库的表数据比较的详细程序流程,两张表是相同的结构,都有整型主键id字段,需要每次从数据库分批取得2000条数据,用于比较,比较操作的同时可以再取2000条数据,等上一次比较完成之后,开始比较,直到比较完所有的数据。比较操作需要比较…...

【系统架构设计师-2025上半年真题】综合知识-参考答案及部分详解(回忆版)

更多内容请见: 备考系统架构设计师-专栏介绍和目录 文章目录 【第1题】【第2题】【第3题】【第4题】【第5题】【第6题】【第7题】【第8题】【第9题】【第10题】【第11题】【第12题】【第13题】【第14题】【第15题】【第16题】【第17题】【第18题】【第19题】【第20~21题】【第…...

八、【ESP32开发全栈指南:UDP客户端】

1. 环境准备 安装ESP-IDF v4.4 (官方指南)确保Python 3.7 和Git已安装 2. 创建项目 idf.py create-project udp_client cd udp_client3. 完整优化代码 (main/main.c) #include <string.h> #include "freertos/FreeRTOS.h" #include "freertos/task.h&…...

旋量理论:刚体运动的几何描述与机器人应用

旋量理论为描述刚体在三维空间中的运动提供了强大而优雅的数学框架。与传统的欧拉角或方向余弦矩阵相比&#xff0c;旋量理论通过螺旋运动的概念统一了旋转和平移&#xff0c;在机器人学、计算机图形学和多体动力学领域具有显著优势。这种描述不仅几何直观&#xff0c;而且计算…...