Vue 3.0中响应式依赖和更新

响应式依赖和更新是Vue 3.0中最重要的机制,其核心代码如下,本文将结合代码对这个设计机制作出一些解释。

// 全局依赖存储:WeakMap<target, Map<key, Set<effect>>>
const targetMap = new WeakMap();// 当前活动的副作用函数(如组件的渲染函数)
let activeEffect = null;// 响应式入口函数
function reactive(target) {return createReactiveObject(target, mutableHandlers);
}// 创建响应式代理对象
function createReactiveObject(target, handlers) {if (typeof target !== 'object' || target === null) {return target; // 非对象直接返回}return new Proxy(target, handlers);
}// Proxy 拦截器配置
const mutableHandlers = {get(target, key, receiver) {track(target, key); // 依赖收集const res = Reflect.get(target, key, receiver);if (typeof res === 'object' && res !== null) {return reactive(res); // 深度响应式}return res;},set(target, key, value, receiver) {const oldValue = target[key];const result = Reflect.set(target, key, value, receiver);if (oldValue !== value) {trigger(target, key); // 触发更新}return result;},// 其他拦截器(deleteProperty/has/ownKeys 等省略)
};// 依赖收集:建立 target.key → effect 的映射
function track(target, key) {if (!activeEffect) return; // 无活动 effect 则退出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()));}if (!dep.has(activeEffect)) {dep.add(activeEffect); // 添加 effect 到依赖集合activeEffect.deps.push(dep); // 反向关联(用于清理)}
}// 触发更新:执行 target.key 的所有依赖 effect
function trigger(target, key) {const depsMap = targetMap.get(target);if (!depsMap) return;const effects = new Set();const dep = depsMap.get(key);if (dep) {dep.forEach(effect => effects.add(effect));}effects.forEach(effect => effect()); // 重新执行副作用函数
}// 副作用函数封装(示例)
function effect(fn) {const effectFn = () => {activeEffect = effectFn;fn();activeEffect = null;};effectFn.deps = []; // 存储所有关联的 dep 集合effectFn();
}

1. 核心数据结构:targetMap

类型:WeakMap<Object, Map<string, Set<Effect>>>

作用:全局存储所有响应式对象的依赖关系。

键:被代理的原始对象Object。

值:一个 Map,其键是对象的属性名,值是一个 Set,存储了与该属性相关的所有副作用函数,如组件的渲染函数。

2. track 函数:依赖收集

触发时机:当访问响应式对象的某个属性时,即触发get操作。

核心流程:

1. 获取当前活动的副作用函数:activeEffect 指向当前正在执行的副作用函数,例如组件渲染函数或 watch 回调;

2. 初始化依赖关系:从 targetMap 中获取目标对象 target 对应的 depsMap,若不存在,则新建一个 Map。从 depsMap 中获取属性 key 对应的依赖集合 dep,若不存在,则新建一个 Set;

3. 建立双向关联:将 activeEffect 添加到 dep 中,表示该属性依赖此副作用函数。将 dep 添加到 activeEffect.deps中,用于后续清理无效依赖,避免内存泄漏;

const obj = reactive({ count: 0 });
effect(() => console.log(obj.count));  
// 执行 effect 时,`activeEffect` 指向该函数
// 访问 obj.count 时触发 track,将 effect 函数添加到 count 的依赖集合中。

3. trigger 函数:触发更新

触发时机:当修改响应式对象的属性时 ,即触发set 操作。

核心流程:

1. 获取目标对象的依赖集合:从 targetMap 中获取 target 对应的 depsMap;

2. 收集所有相关副作用函数:从 depsMap 中获取属性 key 对应的 dep,即该属性的所有依赖函数。将dep中的所有 effect 添加到一个临时集合 effects 中;

3. 执行副作用函数:遍历 effects,依次执行每个 effect,触发视图更新或回调逻辑; 

obj.count++;  // 修改 count 时触发 trigger,执行所有依赖 count 的 effect。

4. 关键设计细节

1. WeakMap 的作用:键是弱引用,当目标对象 target 不再被引用时,targetMap 中对应的条目会被自动垃圾回收,避免内存泄漏; 

2. activeEffect 的管理:通过 effect 函数或组件渲染过程,将当前运行的副作用函数赋值给 activeEffect。执行完毕后,activeEffect 会被重置为 null,确保依赖收集的准确性; 

3. 双向依赖关系:effect.deps 存储了所有与该副作用函数相关的依赖集合dep。当副作用函数被销毁时,可以通过遍历 effect.deps 从所有 dep 中移除该函数,避免无效更新; 

4. 性能优化:使用 Set 存储依赖函数,避免重复添加。触发更新时,通过临时集合 effects 确保每个 effect 只执行一次,避免循环触发; 

5. 总结

依赖收集(Track):通过 track 函数,在属性被访问时记录当前活动的副作用函数,建立属性与副作用函数的关联。

触发更新(Trigger):通过 trigger 函数,在属性被修改时找到所有相关副作用函数并执行,实现数据变化到视图更新的响应式机制。

全局依赖关系:targetMap 作为核心数据结构,通过 WeakMap 和嵌套的 Map/Set,高效管理对象、属性和副作用函数之间的映射关系。

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

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

相关文章

一、内存调优

一、内存调优 什么是内存泄漏 监控Java内存的常用工具 内存泄露的常见场景 内存泄露的解决方案 内存泄露与内存溢出的区别 内存泄露&#xff1a;在Java中如果不再使用一个对象&#xff0c;但是该对象依然在GC ROOT的引用链上&#xff0c;这个对象就不会被垃圾回收器回收&…

Linux /etc/rc.d/init.d/

在传统的 SysV init 系统中&#xff0c;服务启动脚本通常位于 /etc/rc.d/init.d/ 目录下。这些脚本可以直接执行以启动、停止或重启服务&#xff0c;并且可以接受参数如 start, stop, status 等。 如果你想知道位于 /etc/rc.d/init.d/ 目录下的某个脚本文件实际上指向哪里,如果…

S7 200 smart连接Profinet转ModbusTCP网关与西门子1200PLC配置案例

控制要求&#xff1a;使用MODBUSTCP通信进行两台PLC之间的数据交换&#xff0c;由于改造现场不能改动程序&#xff0c;只留出了对应的IQ地址。于是客户决定使用网关进行通讯把数据传到plc。 1、读取服务器端40001~40005地址中的数据&#xff0c;放入到VW200~VW208中&#xff1…

打破传统仓库管理困局:WMS如何重构出入库全流程

引言 在制造业与零售业高速发展的今天&#xff0c;仓库管理仍普遍面临效率低、错发漏发频发、库存数据滞后等痛点。人工登记导致30%的错单率&#xff0c;货位混乱让拣货耗时增加50%&#xff0c;而账实不符引发的二次采购成本更吞噬着企业利润。如何突破传统管理桎梏&#xff1…

Text2SQL在Spark NLP中的实现与应用:将自然语言问题转换为SQL查询的技术解析

概述 SQL 仍然是当前行业中最受欢迎的技能之一 免责声明&#xff1a;Spark NLP 中的 Text2SQL 注释器在 v3.x&#xff08;2021 年 3 月&#xff09;中已被弃用&#xff0c;不再使用。如果您想测试该模块&#xff0c;请使用 Spark NLP for Healthcare 的早期版本。 自新千年伊…

微服务项目->在线oj系统(Java版 - 5)

相信自己,终会成功 微服务代码: lyyy-oj: 微服务 目录 C端代码 用户题目接口 修改后用户提交代码(应用版) 用户提交题目判题结果 代码沙箱 1. 代码沙箱的核心功能 2. 常见的代码沙箱实现方式 3. 代码沙箱的关键问题与解决方案 4. 你的代码如何与沙箱交互&#xff1f; …

Vue3 Element Plus 中el-table-column索引使用问题

在 Element Plus 的 el-table 组件中&#xff0c;使用 scope.index 是不准确的。正确的索引属性应该是 scope.$index。你的代码需要调整为&#xff1a; vue 复制 下载 <el-button type"primary" size"default" text click"onModifyClick(scope…

Ubuntu20.04下使用dpkg方式安装WPS后,将WPS改为中文界面方法

Ubuntu20.04下使用dpkg方式安装WPS后&#xff0c;将WPS改为中文界面方法 说明方法 说明 Ubuntu20.04下使用dpkg方式安装WPS后&#xff0c;打开WPS后&#xff0c;发现界面是英文的&#xff0c;如有需要可以按照下面的方法将其改为中文界面。 方法 cd /opt/kingsoft/wps-offic…

【​​HTTPS基础概念与原理​】​​HTTPS vs HTTP:为什么现代网站必须用HTTPS?

以下是关于 HTTPS vs HTTP 的详细对比分析&#xff0c;涵盖安全性、性能差异及SEO影响&#xff0c;帮助您全面理解为何现代网站必须采用HTTPS&#xff1a; 一、安全性对比&#xff1a;HTTPS 如何解决 HTTP 的致命缺陷 1. HTTP 的安全隐患 • 明文传输&#xff1a;HTTP 数据以明…

算法刷题(Java与Python)1.二分查找

目录 二分查找 思路 总体 细节 问题一&#xff0c;为什么循环的条件是left<right ,为什么要有等号呢 问题二&#xff0c;为什么中间值是left (right - left) / 2 问题三&#xff0c;为什么最后返回的是左边的值呢 情况 1&#xff1a;target 存在于数组中 情况 2&a…

芯片生态链深度解析(二):基础设备篇——人类精密制造的“巅峰对决”

【开篇&#xff1a;设备——芯片工业的“剑与盾”】 当ASML的EUV光刻机以每秒5万次激光脉冲在硅片上雕刻出0.13nm精度的电路&#xff08;相当于在月球表面精准定位一枚二维码&#xff09;&#xff0c;当国产28nm光刻机在华虹产线实现“从0到1”的突破&#xff0c;这场精密制造…

MongoTemplate 基础使用帮助手册

前言 MongoDB 是一种流行的 NoSQL 数据库&#xff0c;适合存储大量的非结构化数据。MongoTemplate 是 Spring Data MongoDB 中的一个核心组件&#xff0c;它提供了一组丰富的 API 来与 MongoDB 进行交互。它封装了许多常见的数据库操作&#xff0c;使开发者能够轻松执行 CRUD 操…

psotgresql18 源码编译安装

环境&#xff1a; 系统&#xff1a;centos7.9 数据库&#xff1a;postgresql18beta1 #PostgreSQL 18 已转向 DocBook XML 构建体系&#xff08;SGML 未来将被弃用&#xff09;。需要安装 XML 工具链&#xff0c;如下&#xff1a; yum install -y docbook5-style-xsl libxsl…

C++编程起步项目

员工信息管理系统 需求 Employee.h #pragma once#include<iostream> #include<string>using namespace std;class Employee { public:int id; // 编号string name; // 姓名string position; // 岗位int deptId; // 部门编号Employee();Employee(int id, string n…

Linux的MySQL头文件和找不到头文件问题解决

头文件 #include <iostream> #include <mysql_driver.h> #include <mysql_connection.h> #include <cppconn/statement.h> #include <cppconn/resultset.h> #include <cppconn/prepared_statement.h> #include <cppconn/exception.h&g…

[ linux-系统 ] 命令行参数 | 环境变量

命令行参数 命令行参数是指用户在启动程序时通过命令行传递给程序的参数。这些参数可以用于控制程序的行为、传递输入数据或配置选项。 在 C/C 中&#xff0c;命令行参数通过 main 函数的参数传递 命令行参数列表 argc:参数的个数 argv[]&#xff1a;参数的清单 为什么要…

新书速览|鸿蒙HarmonyOS NEXT开发之路 卷2:从入门到应用篇

《鸿蒙HarmonyOS NEXT开发之路 卷2&#xff1a;从入门到应用篇》 01 本书内容 《鸿蒙HarmonyOS NEXT开发之路 卷2&#xff1a;从入门到应用篇》是一本深度聚焦HarmonyOS NEXT应用开发的全方位指导书&#xff0c;内容遵循由浅入深的原则展开。全书分为基础知识、应用开发进阶和…

经典密码学和现代密码学的结构及其主要区别(1)凯撒密码——附py代码

密码学是一门通过使用代码和密码来保护信息的艺术与科学&#xff0c;其历史可以追溯到数千年前。古典密码学代表了这一古老学科早期的篇章。早在计算机和现代加密算法出现之前&#xff0c;历史上的各个文明就依靠巧妙的方法来保护机密、安全通信以及获取战略优势。 古典密码学…

Python60日基础学习打卡D30

回顾&#xff1a; 导入官方库的三种手段导入自定义库/模块的方式导入库/模块的核心逻辑&#xff1a;找到根目录&#xff08;python解释器的目录和终端的目录不一致&#xff09; # 直接导入 from random import randint print(randint(1, 10)) # 导入自定义库 import module m…

Linux利用多线程和线程同步实现一个简单的聊天服务器

1. 概述 本文实现一个基于TCP/IP的简单多人聊天室程序。它包含一个服务器端和一个客户端&#xff1a;服务器能够接收多个客户端的连接&#xff0c;并将任何一个客户端发来的消息广播给所有其他连接的客户端&#xff1b;客户端则可以连接到服务器&#xff0c;发送消息并接收来自…