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

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.lryc.cn/news/2378635.html

相关文章:

  • uniapp|实现获取手机摄像头权限,调用相机拍照实现人脸识别相似度对比,拍照保存至相册,多端兼容(APP/微信小程序)
  • JavaScript【7】BOM模型
  • [强化学习的数学原理—赵世钰老师]学习笔记02-贝尔曼方程
  • 使用Spring Boot与Spring Security构建安全的RESTful API
  • 深入理解构造函数,析构函数
  • Day 16
  • 摄影构图小节
  • DAY 28 类的定义和方法
  • RAG数据处理:PDF/HTML
  • 机器学习 day04
  • 蓝牙耳机什么牌子好?倍思值得冲不?
  • 机器学习-人与机器生数据的区分模型测试-数据处理 - 续
  • ESP系列单片机选择指南:结合实际场景的最优选择方案
  • 特斯拉虚拟电厂:能源互联网时代的分布式革命
  • jvm安全点(三)openjdk17 c++源码垃圾回收之安全点结束,唤醒线程
  • Python OOP核心技巧:如何正确选择实例方法、类方法和静态方法
  • 【Linux笔记】nfs网络文件系统与autofs(nfsdata、autofs、autofs.conf、auto.master)
  • 博客打卡-求解流水线调度
  • 基于React的高德地图api教程006:两点之间距离测量
  • 数据库blog1_信息(数据)的处理与效率提升
  • 布隆过滤器介绍及其在大数据场景的应用
  • Ansys 计算刚柔耦合矩阵系数
  • 微服务八股(自用)
  • 指定elf文件dwarf 版本以及查看dwarf版本号
  • Fidder基本操作
  • 项目管理进阶:精读 78页华为项目管理高级培训教材【附全文阅读】
  • [Java] 方法和数组
  • 微软家各种copilot的AI产品:Github copilot、Microsoft copilot
  • KL散度 (Kullback-Leibler Divergence)
  • 深入解析:java.sql.SQLException: No operations allowed after statement closed 报错