【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等,我们会在下一个章节继续探讨。

在这里插入图片描述

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

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

相关文章

正点原子imx6ull-mini不使用网络更新内核系统

参考视频&#xff1a;【【正点原子】Linux网络环境搭建篇】 参考文档&#xff1a;从正点原子官方下载 这几天在学imx6ull写网络驱动检测出网卡&#xff0c;但是一直ping不通ubuntu&#xff0c;电脑还有ubuntu、开发板都处于同一个网段&#xff0c;跟着正点原子的视频试了双网…

用 AI 编程-释放ChatGPT的力量

最近读了本书&#xff0c;是 Sean A Williams 写的&#xff0c;感觉上还是相当不错的。一本薄薄的英文书&#xff0c;还真是写的相当好。如果你想看&#xff0c;还找不到&#xff0c;可以考虑私信我吧。 ChatGPT for Coders Unlock the Power of AI with ChatGPT: A Comprehens…

计数器的原理和应用

一、计数器的原理和应用 要求&#xff1a;每计数三次&#xff0c;数码管值加一 #include<reg51.h> unsigned char s[]{0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F}; unsigned char num0; void initcounter() {TMOD0x06;//0000 0110TH0256-3;TL0256-3;ET01;EA1;T…

java数据结构与算法刷题-----LeetCode34. 在排序数组中查找元素的第一个和最后一个位置

java数据结构与算法刷题目录&#xff08;剑指Offer、LeetCode、ACM&#xff09;-----主目录-----持续更新(进不去说明我没写完)&#xff1a;https://blog.csdn.net/grd_java/article/details/123063846 文章目录 二分查找 二分查找 解题思路&#xff1a;时间复杂度O( l o g 2 …

vue实现把Ox格式颜色值转换成rgb渐变颜色值(开箱即用)

图示&#xff1a; 核心代码&#xff1a; //将0x格式的颜色转换为Hex格式&#xff0c;并计算插值返回rgb颜色 Vue.prototype.$convertToHex function (colorCode1, colorCode2, amount) {// 确保输入是字符串&#xff0c;并检查是否以0x开头let newCode1 let newCode2 if (t…

五、基于KubeAdm搭建多节点K8S集群

如需查阅上一步骤,请点击下面链接:四、戴尔R630本地服务器Linux Centos7.9系统安装docker-ce-20.10.10-3.el7版本-CSDN博客文章浏览阅读727次,点赞12次,收藏13次。1、准备工作3、Linux Centos7.9系统的iDRAC远程管理、网络设置、SecureCRT远程登录终端、企业级静态ip地址配…

网络安全新视角:数据可视化的力量

在当今数字化时代&#xff0c;网络安全已成为各大企业乃至国家安全的重要组成部分。随着网络攻击的日益复杂和隐蔽&#xff0c;传统的网络安全防护措施已难以满足需求&#xff0c;急需新型的解决方案以增强网络防护能力。数据可视化技术&#xff0c;作为一种将复杂数据转换为图…

中国象棋AI在线对弈游戏源码

源码介绍 这是一款html5小游戏&#xff0c;主要功能在于js&#xff0c;带一套皮肤、内置ai算法&#xff0c;有能力的可以自行修改。 源码截图 下载地址 链接&#xff1a;https://pan.baidu.com/s/1fYp1HWsd91nJOdX1M8RFtQ?pwdh2iz 提取码&#xff1a;h2iz

自动化测试——面试题整理

1.selenium中有哪些元素定位方式&#xff1f; 元素定位的目的&#xff1a;使用Web自动化操作元素&#xff0c;让程序操作指定元素&#xff0c;就必须找到此元素 共有8种定位方式 与name有关的&#xff1a;name、class_name、tag_name【标签名定位】与link相关的&#xff1a;…

C语言最大公约数(辗转相除法)

输入两个整数&#xff0c;求他们的最大公约数&#xff1a; 如果我们不用辗转相除法的话&#xff0c;两个整数的最大公约数&#xff0c;我们就可以定义一个整数为两个整数中最小的那个数&#xff0c;然后两个整数一起除我们新定义的整数&#xff0c;如果都除尽了&#xff0c;这…

云服务器8核32G配置租用优惠价格94元/月、1362元一年

8核32G云服务器京东云轻量云主机价格94元1个月、282元3个月、673元6个月、1362元一年&#xff0c;配置8C32G-100G SSD系统盘-10M带宽-2000G月流量 华北-北京&#xff0c;京东云优惠活动 yunfuwuqiba.com/go/jd 活动链接打开如下图&#xff1a; 8核32G云服务器京东云轻量云主机价…

COLMAP 【Feature matching】特征匹配参数解释

&#xff08;Windows&#xff09;Colmap 具体使用教程可参考我的这篇博文 windowscolmap实现SFM三维重建位姿估计-CSDN博客 下面只是matching参数解释 Matching这个阶段很重要&#xff0c;匹配方式不同会对最终结果影响很大&#xff0c;要根据实际情况选择合适的匹配方式。下…