深挖vue3基本原理之一 —— 响应式系统(Reactivity System)

响应式系统(Reactivity System)

1.1 基于 Proxy 的响应式代理

在 Vue 3 中,响应式系统的核心是使用 ES6 的 Proxy 来替代 Vue 2 里的 Object.defineProperty 方法,以此实现更加全面和强大的响应式追踪功能。下面我们来详细剖析这个过程。

响应式代理的实现代码
const reactive = (target) => {
  return new Proxy(target, {
    get(target, key, receiver) {
      track(target, key) // 依赖收集
      return Reflect.get(target, key, receiver)
    },
    set(target, key, value, receiver) {
      Reflect.set(target, key, value, receiver)
      trigger(target, key) // 触发更新
      return true
    }
  })
}
代码详细解释
  • reactive 函数:该函数接收一个目标对象 target 作为参数,返回一个使用 Proxy 代理后的新对象。Proxy 是 ES6 提供的一个强大特性,它可以拦截并重新定义对象的基本操作,比如属性的读取和设置。
  • get 拦截器:当访问代理对象的属性时,会触发 get 拦截器。
    • track(target, key):调用 track 函数进行依赖收集。依赖收集的目的是记录哪些副作用函数依赖于当前访问的属性,以便在属性值发生变化时能够通知这些副作用函数进行更新。
    • Reflect.get(target, key, receiver):使用 Reflect.get 方法获取目标对象的属性值。Reflect 是 ES6 新增的一个内置对象,它提供了一系列用于操作对象的方法,与 Proxy 配合使用可以更方便地实现对象的拦截操作。
  • set 拦截器:当设置代理对象的属性时,会触发 set 拦截器。
    • Reflect.set(target, key, value, receiver):使用 Reflect.set 方法设置目标对象的属性值。
    • trigger(target, key):调用 trigger 函数触发更新。当属性值发生变化时,需要通知所有依赖该属性的副作用函数重新执行。
相比 Object.defineProperty 的优势
  • 支持数组索引修改和 length 变化检测:在 Vue 2 中,使用 Object.defineProperty 来实现响应式时,对于数组的一些操作(如通过索引修改元素、修改 length 属性)无法直接检测到变化。而在 Vue 3 中,使用 Proxy 可以轻松地拦截这些操作,从而实现对数组的全面响应式追踪。
const arr = reactive([1, 2, 3]);
arr[0] = 10; // 可以正常触发更新
arr.length = 2; // 也可以正常触发更新
  • 自动追踪新增对象属性(无需 Vue.set:在 Vue 2 中,如果要给响应式对象新增一个属性,需要使用 Vue.set 方法才能让新增的属性也具有响应式特性。而在 Vue 3 中,由于使用了 Proxy,可以自动追踪对象新增的属性,无需额外的操作。
const obj = reactive({ a: 1 });
obj.b = 2; // 新增属性 b 会自动具有响应式特性
  • 深度监听嵌套对象(Lazy 模式,按需激活):Vue 3 的响应式系统会对嵌套对象进行深度监听,但采用的是 Lazy 模式,即只有在访问嵌套对象的属性时才会激活对该嵌套对象的响应式追踪,这样可以提高性能。
const nestedObj = reactive({
  inner: {
    c: 3
  }
});
// 当访问 nestedObj.inner.c 时,才会激活对 inner 对象的响应式追踪
1.2 依赖管理机制

Vue 3 的响应式系统采用了三层依赖管理体系,通过 WeakMapMapSet 来高效地管理依赖关系。

三层依赖管理体系的结构
  • TargetMap:是一个 WeakMap,用于存储目标对象到键的映射。WeakMap 的键必须是对象,并且这些对象是弱引用,即如果对象没有其他引用指向它,它可以被垃圾回收,这样可以避免内存泄漏。
  • DepsMap:是一个 Map,用于存储键到依赖集合的映射。每个键对应一个依赖集合,该集合存储了所有依赖于该键的副作用函数。
  • Dep:是一个 Set,用于存储具体的副作用函数。Set 是一种无序且唯一的数据结构,确保每个副作用函数只被存储一次。
依赖管理的代码实现
type Dep = Set<ReactiveEffect>;
type KeyToDepMap = Map<any, Dep>;
const targetMap = new WeakMap<any, KeyToDepMap>();

function track(target: object, key: unknown) {
  if (!activeEffect) return;
  let depsMap = targetMap.get(target);
  if (!depsMap) {
    targetMap.set(target, (depsMap = new Map()));
  }
  let dep = depsMap.get(key);
  if (!dep) {
    depsMap.set(key, (dep = new Set()));
  }
  dep.add(activeEffect);
}
代码详细解释
  • 类型定义
    • Dep:定义为 Set<ReactiveEffect>,表示一个存储副作用函数的集合。
    • KeyToDepMap:定义为 Map<any, Dep>,表示一个存储键到依赖集合的映射。
  • targetMap:是一个全局的 WeakMap,用于存储所有目标对象的依赖信息。
  • track 函数:用于进行依赖收集。
    • if (!activeEffect) return;:如果当前没有活跃的副作用函数,则直接返回,不进行依赖收集。
    • let depsMap = targetMap.get(target);:从 targetMap 中获取目标对象对应的 DepsMap
    • if (!depsMap) { targetMap.set(target, (depsMap = new Map())); }:如果 DepsMap 不存在,则创建一个新的 Map 并存储到 targetMap 中。
    • let dep = depsMap.get(key);:从 DepsMap 中获取键对应的依赖集合。
    • if (!dep) { depsMap.set(key, (dep = new Set())); }:如果依赖集合不存在,则创建一个新的 Set 并存储到 DepsMap 中。
    • dep.add(activeEffect);:将当前活跃的副作用函数添加到依赖集合中。
1.3 副作用调度

Vue 3 的响应式系统基于调度器实现了异步更新队列,以提高性能和避免不必要的重复更新。

异步更新队列的实现代码
const queue = new Set();
let isFlushing = false;

function queueJob(job) {
  queue.add(job);
  if (!isFlushing) {
    isFlushing = true;
    Promise.resolve().then(() => {
      try {
        queue.forEach(job => job());
      } finally {
        queue.clear();
        isFlushing = false;
      }
    });
  }
}
代码详细解释
  • queue:是一个 Set,用于存储需要执行的副作用函数。使用 Set 可以确保每个副作用函数只被存储一次,避免重复执行。
  • isFlushing:是一个布尔值,用于标记当前是否正在执行更新队列中的副作用函数。
  • queueJob 函数:用于将副作用函数添加到更新队列中。
    • queue.add(job):将副作用函数添加到 queue 中。
    • if (!isFlushing) { ... }:如果当前没有正在执行更新队列中的副作用函数,则启动异步更新。
      • isFlushing = true;:标记为正在执行更新。
      • Promise.resolve().then(() => { ... }):使用 Promise 实现异步执行。在微任务队列中执行更新队列中的副作用函数。
      • try { queue.forEach(job => job()); } finally { queue.clear(); isFlushing = false; }:遍历更新队列,执行每个副作用函数。执行完毕后,清空队列并将 isFlushing 标记为 false,表示更新完成。

通过这种异步更新队列的方式,Vue 3 可以将多次属性变化引起的副作用函数执行合并为一次,从而提高性能。例如,在短时间内多次修改响应式对象的属性,只会触发一次副作用函数的执行。

综上所述,Vue 3 的响应式系统通过基于 Proxy 的响应式代理、三层依赖管理机制和副作用调度,实现了高效、全面的响应式追踪和更新功能。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

python知识和项目经验

一些功能的实现 从.py文件中获取函数对象和参数 的字典 在给定的Python脚本中&#xff0c;通过模块导入和反射机制&#xff0c;如何动态获取包含模型函数的模块中的函数及其默认参数&#xff0c;并构建一个字典以便后续使用&#xff1f; 解决方案 test.py # test.py impor…

Unity下ML-Agents第一个示例

本文写于2025年2月12日&#xff0c;需要提前安装好Anaconda。按文中步骤测试了两次都可正常运行。 一、准备Python端 1.下载并解压 ML-Agents Release 22&#xff08;使用git clone大概率会失败&#xff09; 解压路径为 C:\Users\Administrator&#xff08;Administrator为电…

FastExcel + Java:打造高效灵活的Excel数据导入导出解决方案

作者&#xff1a;后端小肥肠 &#x1f347; 我写过的文章中的相关代码放到了gitee&#xff0c;地址&#xff1a;xfc-fdw-cloud: 公共解决方案 &#x1f34a; 有疑问可私信或评论区联系我。 &#x1f951; 创作不易未经允许严禁转载。 姊妹篇&#xff1a; 基于AOP的数据字典实现…

解决IDEA中gitlab登录只有token选项,没有账号密码选项

如图&#xff0c;当点击gitlab账户登录的时候&#xff0c;只显示server和token&#xff0c;而没有账号选项。期望通过账号密码登录。 解决方式&#xff1a; 插件 - GitLab - 禁用即可。

AI语言模型的技术之争:DeepSeek与ChatGPT的架构与训练揭秘

云边有个稻草人-CSDN博客 目录 第一章&#xff1a;DeepSeek与ChatGPT的基础概述 1.1 DeepSeek简介 1.2 ChatGPT简介 第二章&#xff1a;模型架构对比 2.1 Transformer架构&#xff1a;核心相似性 2.2 模型规模与参数 第三章&#xff1a;训练方法与技术 3.1 预训练与微调…

PHP 中的除以零错误

除以零错误&#xff08;Division by zero&#xff09;是指数字除以零的情况&#xff0c; 这在数学上是未定义的。在 PHP 中&#xff0c;处理这种错误的方式取决于 PHP 版本&#xff1a; PHP 7&#xff1a; 使用 / 运算符会产生一个警告 (E_WARNING) 并返回 false。 使用 intd…

【设计模式】01- 一文理解常用设计模式-“创建型模式”篇

一、前言 最近在复习设计模式&#xff0c;撰写、整理了内容和代码片段&#xff0c;和大家一起交流学习。 设计模式是软件设计中常见问题的典型解决方案。 二、模式分类 模式可以根据其意图或目的来分类。常见的设计模式包括&#xff1a; 创建型模式提供创建对象的机制&#x…

数据结构-链式二叉树

文章目录 一、链式二叉树1.1 链式二叉树的创建1.2 根、左子树、右子树1.3 二叉树的前中后序遍历1.3.1前(先)序遍历1.3.2中序遍历1.3.3后序遍历 1.4 二叉树的节点个数1.5 二叉树的叶子结点个数1.6 第K层节点个数1.7 二叉树的高度1.8 查找指定的值(val)1.9 二叉树的销毁 二、层序…

游戏引擎学习第99天

仓库:https://gitee.com/mrxiao_com/2d_game_2 黑板&#xff1a;制作一些光场(Light Field) 当前的目标是为游戏添加光照系统&#xff0c;并已完成了法线映射&#xff08;normal maps&#xff09;的管道&#xff0c;但还没有创建可以供这些正常映射采样的光场。为了继续推进&…

LSTM变种模型

GRU GRU简介 门控循环神经网络 (Gated Recurrent Neural Network&#xff0c;GRNN) 的提出&#xff0c;旨在更好地捕捉时间序列中时间步距离较大的依赖关系。它通过可学习的门来控制信息的流动。其中&#xff0c;门控循环单元 (Gated Recurrent Unit &#xff0c; GRU) 是…

业务开发 | 基础知识 | Maven 快速入门

Maven 快速入门 1.Maven 全面概述 Apache Maven 是一种软件项目管理和理解工具。基于项目对象模型的概念&#xff08;POM&#xff09;&#xff0c;Maven 可以从中央信息中管理项目的构建&#xff0c;报告和文档。 2.Maven 基本功能 因此实际上 Maven 的基本功能就是作为 Ja…

新一代SCADA: 宏集Panorama Suite 2025 正式发布,提供更灵活、符合人体工学且安全的应用体验

宏集科技宣布正式推出全新Panorama Suite 2025 SCADA软件&#xff01;全新版本标志着 Panorama Suite的一个重要里程碑&#xff0c;代表了从 Panorama Suite 2022 开始并跨越三个版本&#xff08;2022、2023、2025&#xff09;的开发过程的顶峰。 此次重大发布集中在六个核心主…

PAT乙级真题 — 1080 MOOC期终成绩(java)【测试点3超时】

对于在中国大学MOOC&#xff08;http://www.icourse163.org/ &#xff09;学习“数据结构”课程的学生&#xff0c;想要获得一张合格证书&#xff0c;必须首先获得不少于200分的在线编程作业分&#xff0c;然后总评获得不少于60分&#xff08;满分100&#xff09;。总评成绩的计…

【Oracle篇】浅谈执行计划中的多表连接(含内连接、外连接、半连接、反连接、笛卡尔连接五种连接方式和嵌套、哈希、排序合并三种连接算法)

&#x1f4ab;《博主介绍》&#xff1a;✨又是一天没白过&#xff0c;我是奈斯&#xff0c;从事IT领域✨ &#x1f4ab;《擅长领域》&#xff1a;✌️擅长阿里云AnalyticDB for MySQL(分布式数据仓库)、Oracle、MySQL、Linux、prometheus监控&#xff1b;并对SQLserver、NoSQL(…

TCP 端口号为何位于首部前四个字节?协议设计的智慧与启示

知乎的一个问题很有意思&#xff1a;“为什么在TCP首部中要把TCP的端口号放入最开始的四个字节&#xff1f;” 这种问题很适合我这种搞历史的人&#xff0c;大年初一我给出了一个简短的解释&#xff0c;但仔细探究这个问题&#xff0c;我们将会获得 TCP/IP 被定义的过程。 文…

oracle表分区--范围分区

文章目录 oracle表分区分区的原因分区的优势oracle表分区的作用oracle表分区类型一、范围分区二、 创建分区表和使用&#xff1a;1、按照数值范围划分2、按照时间范围3、MAXVALUE2. 向现有表添加新的分区3、 分区维护和重新组织&#xff08;合并/删除&#xff09; oracle表分区…

蓝桥杯(B组)-每日一题(求最大公约数最小公倍数)

题目&#xff1a; 代码展现&#xff1a; #include<iostream> using namespace std; int main() {int m,n,x,y;cin>>m>>n;//输入两个整数int b;bm%n;//取余数xm;//赋值yn;while(b)//当余数不为0的时候{xy;//辗转相除求最小公约数yb;bx%y;}cout<<y<&…

基于STM32的学习环境控制系统设计

&#x1f91e;&#x1f91e;大家好&#xff0c;这里是5132单片机毕设设计项目分享&#xff0c;今天给大家分享的是学习环境控制。 设备的详细功能见网盘中的文章《21、基于STM32的学习环境控制系统设计》&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1uWSZX2zbZwy9sY…

WPS接入DeepSeek模型

1.wps 下载安装 WPS-支持多人在线协作编辑Word、Excel和PPT文档_WPS官方网站 &#xff08;最好是安装最新的wps&#xff09; 2.offieceAi工具下载安装 软件下载 | OfficeAI助手 下载后安装下载下来的两个工具。安装路径可以自行修改 3.打开WPS,点击文件-》 选项-》信任中心 勾…

4. React 中的 CSS

用例中的干净的脚手架的创建可以参考另一篇文章&#xff1a;3.React 组件化开发React官方并没有给出在React中统一的样式风格&#xff1a; 由此&#xff0c;从普通的css&#xff0c;到css modules&#xff0c;再到css in js&#xff0c;有几十种不同的解决方案&#xff0c;上百…