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

Vue3源码reactivity响应式篇之EffectScope

概述

EffectScope是Vue3中一个响应式系统的辅助类,用于管理副作用(effect)的作用域。它可以帮助我们更好地组织和管理多个effect,便于一起停止或暂停以及恢复,避免了全局状态的污染和管理的复杂性。

每一个vue组件的实例上都会挂载一个EffectScope的实例scope,该挂载动作会在createComponentInstance中进行。在组件被激活挂载时,会调用scope.on()方法,将当前作用域设置为活动作用域;而在组件被卸载或者销毁时,会调用scope.stop()方法。

源码解析

let activeEffectScope;class EffectScope {constructor(detached = false) {this.detached = detached; // 表示该作用域是否独立(不嵌套在父作用域中)this._active = true; //表示作用域是否激活this._on = 0; // 一个计数器,用于记录当前作用域被开启的次数(通过on方法)this.effects = []; // 存储属于该作用域的effectthis.cleanups = []; // 存储清理函数,当作用域停止时会执行这些清理函数this._isPaused = false; // 表示作用域是否被暂停this.parent = undefined; //指向父作用域this.scopes = undefined; //存储嵌套的子作用域this.index = undefined; // 在父作用域的scopes数组中的索引this.prevScope = undefined; // 在调用 on 方法时,用于保存当前的activeEffectScope,以便在off时恢复,上一个作用域(用于作用域链)this.parent = activeEffectScope; //将父作用域指向激活的作用域if (!detached && activeEffectScope) {// 若该作用域不独立且当前存在激活的作用域,则将该作用域存放到激活作用域(父作用域)的子作用域的末尾,并设置该作用域的索引this.index = (activeEffectScope.scopes || (activeEffectScope.scopes = [])).push(this) - 1;}}// 暂停作用域pause() {if (this._active) {this._isPaused = true;let i, l;if (this.scopes) {for (i = 0, l = this.scopes.length; i < l; i++) {this.scopes[i].pause();}}for (i = 0, l = this.effects.length; i < l; i++) {this.effects[i].pause();}}}// 恢复作用域resume() {if (this._active) {if (this._isPaused) {this._isPaused = false;let i, l;if (this.scopes) {for (i = 0, l = this.scopes.length; i < l; i++) {this.scopes[i].resume();}}for (i = 0, l = this.effects.length; i < l; i++) {this.effects[i].resume();}}}}// 在作用域内运行函数run(fn) {if (this._active) {const currentEffectScope = activeEffectScope;try {activeEffectScope = this;return fn();} finally {activeEffectScope = currentEffectScope;}}}// 开启作用域on() {if (++this._on === 1) {this.prevScope = activeEffectScope;activeEffectScope = this;}}// 关闭作用域off() {if (this._on > 0 && --this._on === 0) {activeEffectScope = this.prevScope;this.prevScope = void 0;}}// 停止作用域stop(fromParent) {if (this._active) {this._active = false;let i, l;for (i = 0, l = this.effects.length; i < l; i++) {this.effects[i].stop();}this.effects.length = 0;for (i = 0, l = this.cleanups.length; i < l; i++) {this.cleanups[i]();}this.cleanups.length = 0;if (this.scopes) {for (i = 0, l = this.scopes.length; i < l; i++) {this.scopes[i].stop(true);}this.scopes.length = 0;}if (!this.detached && this.parent && !fromParent) {const last = this.parent.scopes.pop();if (last && last !== this) {this.parent.scopes[this.index] = last;last.index = this.index;}}this.parent = void 0;}}
}
核心方法

核心方法主要分为三类:作用域控制(pause/resume/stop)、作用域运行(run)和作用域激活(on/off)

作用域控制
  • pause

pause方法就是用于暂停作用域及其所有子作用域和副作用。先是判断作用域是否处于激活状态,若是激活状态,则修改作用域的暂停状态this._isPaused,改为true,然后遍历子作用域和副作用数组,调用其stop方法实现暂停。

  • resume

resume方法用于暂停状态的作用域及其所有子作用域和副作用。和pause方法是相反的操作,会修改this.isPausedfalse,遍历调用所有子作用域和副作用的resume方法。

  • stop

stop方法用于停止作用域:清理所有副作用和子作用域,并且从父作用域中移除自身。

stop方法会停止所有effect,执行所有cleanups,并递归停止所有子作用域。如果该作用域不是独立的且由副作用域,并且不是由副作用域触发的停止,则从副作用域的scopes数组中移除自己,并调整数组。

作用域运行
  • run

run方法用于在该作用域内运行函数,这样函数内创建的effect会自动注册到该作用域中。主要就是将该作用域切换为激活作用域,然后执行fn,最后恢复激活作用域为之前的。

作用域激活
  • on

on方法用于激活作用域,就是增加计数器,若计数器增加后等于 1,则将该作用域作为激活作用域。

  • off

off方法和on方法相对,用于关闭作用域,减少计数器,若计数器大于 0,且减少后等于0,则恢复激活作用域为前值。

通过on/off方法,可以临时切换当前激活的作用域,这样可以实现在onoff切换之间创建effect会注册到该作用域中。

辅助方法

EffectScope类相关的三个辅助方法:effectScopegetCurrentScopeonScopeDispose

  • effectScope

effectScope方法就是返回EffectScope的实例对象。

function effectScope(detached) {return new EffectScope(detached);
}
  • getCurrentScope

getCurrentScope方法用于获取当前活跃(激活)的作用域

function getCurrentScope() {return activeEffectScope;
}
  • onScopeDispose

onScopeDispose方法会判断若当前作用域存在,则将函数fn注入到作用域的清理数组cleanups中。

function onScopeDispose(fn, failSilently = false) {if (activeEffectScope) {activeEffectScope.cleanups.push(fn);}
}

文章转载自:

http://dnULaJYI.mtzyr.cn
http://R43hLFIf.mtzyr.cn
http://IIUVYAF0.mtzyr.cn
http://mIWt0sxY.mtzyr.cn
http://4FExsGQR.mtzyr.cn
http://BTfTe78a.mtzyr.cn
http://M7boaWlf.mtzyr.cn
http://CwMMvWz7.mtzyr.cn
http://tUHzVHgp.mtzyr.cn
http://VGhdnGr0.mtzyr.cn
http://E5EkXwY5.mtzyr.cn
http://JnbLHgVm.mtzyr.cn
http://vNUHG4ci.mtzyr.cn
http://jjlQXDFi.mtzyr.cn
http://wk9rGw0P.mtzyr.cn
http://WARAJfgs.mtzyr.cn
http://QtTC6Nlx.mtzyr.cn
http://cRuEPVVQ.mtzyr.cn
http://XSOj67uJ.mtzyr.cn
http://fMLola45.mtzyr.cn
http://CPGQVrYf.mtzyr.cn
http://HBzCBpO7.mtzyr.cn
http://5vvL52Mx.mtzyr.cn
http://M7hmbfdX.mtzyr.cn
http://Fg0SvoFP.mtzyr.cn
http://aUrAg3qa.mtzyr.cn
http://Jp5OSjFh.mtzyr.cn
http://6nmMxg5L.mtzyr.cn
http://u4M2T760.mtzyr.cn
http://iY0VgEgm.mtzyr.cn
http://www.dtcms.com/a/368371.html

相关文章:

  • 从Java全栈到前端框架:一位程序员的实战之路
  • 【Java实战㉖】深入Java单元测试:JUnit 5实战指南
  • 【AI论文】Robix:一种面向机器人交互、推理与规划的统一模型
  • C++(Qt)软件调试---bug排查记录(36)
  • yolov8部署在一台无显卡的电脑上,实时性强方案
  • Alibaba Cloud Linux 3 安装Docker
  • SQL面试题及详细答案150道(61-80) --- 多表连接查询篇
  • 详细解读Docker
  • 【OJ】C++ vector类OJ题
  • 【数据库】MySQL 数据库创建存储过程及使用场景详解
  • Ubuntu22.04-ROS2下navgation2编译到运行
  • OpenLayers常用控件 -- 章节四:图层控制与切换教程
  • [ubuntu][C++]onnxruntime安装cpu版本后测试代码
  • 一个专为地图制图和数据可视化设计的在线配色网站,可以助你制作漂亮的地图!
  • 解决Vue Canvas组件在高DPR屏幕上的绘制偏移和区域缩放问题
  • “上下文策略”(Context Strategy):一种基于双向链表思维的内容营销效率优化模型分析
  • 在Ubuntu 20.04的服务器上查找的服务器的IP地址
  • 用 Cursor AI 快速开发你的第一个编程小程序
  • 自动化运维-ansible中对roles的创建与使用
  • 《Ceph集群数据同步异常的根因突破与恢复实践》
  • 从零开始的云计算生活——第五十九天,基于Jenkins自动打包并部署Tomcat环境
  • 串口通信的学习
  • 企业为何仍困在“数据孤岛”?——从iPaaS重构信息流的实践路径
  • MySQL 主从复制详解:部署与进阶配置
  • 一笔成形,秒绘标准图!Pen Kit重构“自然书写”体验
  • 解决IntelliJ IDEA 提交代码时无复选框问题
  • MyBatisX代码生成插件在IDEA中的安装配置、连接数据库表生成代码快速开发示例
  • Docker跨架构部署实操第二弹
  • VSCode+MobaXterm+X11可视化界面本地显示
  • FastGPT源码解析 Agent 大模型对接接口和使用详解