webpack源码分析——tapable中before和stage如何改变执行顺序

一、Before用法

Before 用法

before 属性的值可以传入一个数组或者字符串,值为注册事件对象时的名称,它可以修改当前事件函数在传入的事件名称对应的函数之前进行执行。

示例

 let hook = new SyncWaterfallHook(['arg1']);

 hook.tap('tap1', (arg)=> {
	console.log('tap1', arg);
	return '返回值';
 });

 hook.tap({
	name: 'tap2',
	before: 'tap1' // 这里调换了顺序,把 tap2 执行顺序调整到 tap1 之前
 }, (arg)=> {
	console.log('tap2', arg);
	return '返回2';
 })

 hook.tap('tap3', (arg)=> {
	console.log('tap3', arg);
	return '返回3';
 })

 hook.call('yun');

请添加图片描述
原理描述

tap 函数把注册函数以一下方式放入taps队列

第一次调用tap函数进行注册函数时:

  1. 使用 _insert 函数完成注册函数进入队列
  2. 因为此时taps队列中还没有函数,所以直接当前注册函数通过 this.taps[i] = item; 推入队列中

第二次调用tap函数进行注册函数时:

  1. 使用 _insert 函数完成注册函数进入队列
  2. 如果taps 中有函数,则进入 while 循环
  3. 把taps队列中最后一个函数(下标为0的函数)取出赋值给x变量中并为队列长度延长一个位置后(当前队列长度为2),把x变量赋值给刚才延长的位置上(刚才延长的位置下标为1)
  4. 把要注册的函数放入下标为1的位置

第三、四…次函数注册和第二次注册处理逻辑一致(只是下标有所不同)。

示例:

tap2是要插入的函数
循环前未插入tap2时:taps:[‘tap1’]
循环前未准备插入tap2时:taps:[‘tap1’,‘tap1’]
不再循环完成插入时:taps:[‘tap1’, ‘tap2’]

tap3是要插入的函数
循环前未插入tap3时:taps:[‘tap1’, ‘tap2’]
循环前未准备插入tap3时:taps:[‘tap1’,‘tap2’,‘tap2’]
不再循环完成插入时:taps:[‘tap1’,‘tap2’, ‘tap3’]

当使用 before 进行改变插入顺序时逻辑如下

第一次插入注册函数和上方一致

第二次调用tap函数进行注册函数时:

  1. 使用 _insert 函数完成注册函数进入队列
  2. taps 中已经有函数了,因此进入 while 循环
  3. 把taps队列中最后一个函数取出赋值给x变量中并为队列长度延长一个位置后,把x变量赋值给刚才延长的位置上
  4. 判断是否存在before,如果不存在就没有改变插入顺序
  5. 如果存在,判断当前x.name是不是要被插入的函数
  6. 如果是则continue
  7. 如果循环条件不满足则退出循环
  8. 最后插入被插入函数的前面

第三、四…次函数注册和第二次注册处理逻辑一致。

示例:

tap3是要插入tap1的函数前面
循环前未插入tap3时:taps:[‘tap1’, ‘tap2’]
循环前未准备插入tap3时:taps:[‘‘tap1’,‘tap2’,‘tap2’]
循环前未准备插入tap3时:taps:[’‘tap1’,‘tap1’,‘tap2’]
不再循环完成插入时:taps:[‘tap3’,‘tap1’, ‘tap2’]

二、stage 用法

stage 用法

stage 类型是数字,数字越大事件回调执行的越晚,支持传入负数,不传时默认为0.

示例

let hook = new SyncWaterfallHook(['arg1']);

 hook.tap('tap1', (arg)=> {
	console.log('tap1', arg);
	return '返回值';
 });

 hook.tap({
	name: 'tap2',
	stage: 2
 }, (arg)=> {
	console.log('tap2', arg);
	return '返回2';
 })

 hook.tap('tap3', (arg)=> {
	console.log('tap3', arg);
	return '返回3';
 })

 hook.tap({
	name: 'tap4',
	stage: 1
 }, (arg)=> {
	console.log('tap4', arg);
	return '返回4';
 })

 hook.call('yun');

请添加图片描述
原理描述
原理基本和Before一致。

示例:

已知tap1,tap2 已在taps队列中 taps:[‘tap1-stage:0’, ‘tap2-stage:0’]
先插入tap3-stage3和tap4-stage2
循环前未插入tap3时:taps:[‘‘tap1-stage:0’, ‘‘tap2-stage:0’]
循环前未准备插入tap3时:taps:[’‘tap1-stage:0’,’‘tap2-stage:0’,‘‘tap2-stage:0’]
不再循环完成插入时:taps:[’‘tap1-stage:0’,‘‘tap2-stage:0’,’‘tap3-stage:3’]

循环前未插入tap4时:taps:[‘‘tap1-stage:0’,’‘tap2-stage:0’,‘‘tap3-stage:3’]
循环前未准备插入tap4时:taps:[’‘tap1-stage:0’,‘‘tap2-stage:0’,’‘tap3-stage:3’, ‘‘tap3-stage:3’]
循环前未准备插入tap4时:taps:[’‘tap1-stage:0’,‘‘tap2-stage:0’,’‘tap3-stage:0’, ‘‘tap3-stage:3’]
不再循环完成插入时:taps:[’‘tap1-stage:0’,‘‘tap2-stage:0’,’“'tap4-stage:2”,‘tap3-stage:3’]

三、源码

_resetCompilation() {
	this.call = this._call;
	this.callAsync = this._callAsync;
	this.promise = this._promise;
}

_insert(item) {
		this._resetCompilation(); // 重置有关配置
		let before;
		if (typeof item.before === "string") { // 判断传入的 before 字段类型,下方统一为Set类型
			before = new Set([item.before]);
		} else if (Array.isArray(item.before)) {
			before = new Set(item.before);
		}
		let stage = 0;
		if (typeof item.stage === "number") {
			stage = item.stage;
		}
		let i = this.taps.length; // 获取注册的tap函数个数
		while (i > 0) {
			i--;
			const x = this.taps[i];
			this.taps[i + 1] = x; 
			const xStage = x.stage || 0;
			if (before) { // 判断是否可以插入
				if (before.has(x.name)) { // 当前x.name是否是被插入的函数
					before.delete(x.name);
					continue; // 继续循环
				}
				if (before.size > 0) {
					continue; // 继续循环
				}
			}
			if (xStage > stage) {
				continue;
			}
			i++; 
			break;
		}
		this.taps[i] = item;
	}

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

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

相关文章

安装 node 错误的配置环境变量之后使用 npm 报错

安装 node 错误的配置环境变量之后使用 npm 报错 node:internal/modules/cjs/loader:1147 throw err; ^ Error: Cannot find module ‘F:\ACodeTools\Node\node_modules\npm\bin\node_modules\npm\bin\npm-cli.js’ at Module._resolveFilename (node:internal/modules/cjs/loa…

Git推送本地仓库至阿里云仓库

Git推送本地仓库至阿里云仓库 1.安装Git 参考Git安装详解 2.生成 SSH 密钥 基于RSA算法SSH 密钥 1.管理员权限运行Git Bash 2.输入生成密钥指令点击回车,选择 SSH 密钥生成路径。 $ ssh-keygen -t rsa -C "2267521563qq.com"3.以 RSA算法为例&…

TMGM官网平台联合英超豪门切尔西

TMGM官网平台联合英超豪门切尔西 TMGM澳洲总部客户经理 🌏:TMGM818 TMGM中国官网【TMGM558】TMGM联合英超豪门切尔西俱乐部深度合作,去年全球客户成交额突破4650亿美元,在泰国曼谷周杰伦演唱会唯一平台品牌赞助商,作为…

IOC中Bean的生命周期

生命周期的各个阶段: 可以分为三个阶段:产生-使用-销毁 又可以分四个阶段:四个阶段 实例化 ->属性注入->初始化 ->销毁 实例化后到使用的初始化过程: 属性赋值 ->处理各种Aware接口->实现BeanPostProcessor的b…

数据结构/C++:二叉搜索树

数据结构/C:二叉搜索树 概念模拟实现结构分析插入中序遍历查找删除析构函数拷贝构造赋值重载递归查找递归插入递归删除 总代码展示 概念 二叉搜索树(BST - Binary Search Tree)是一种特殊的二叉树,每个顶点最多可以有两个子节点。…

逆向案例四:360k静态和精灵数据动态AES解密,用js的方法

一、360K 网页链接:https://www.36kr.com/p/2672600261670407 页面中有静态的需要解密的内容,确定html包,确定方法 1.1方法步骤 在下方的搜索中输入decrypt(或者关键字window.initialState ,进入js文件 在AES.decrypt处打上断点&#xff0…

【Java项目介绍和界面搭建】拼图小游戏——键盘、鼠标事件

🍬 博主介绍👨‍🎓 博主介绍:大家好,我是 hacker-routing ,很高兴认识大家~ ✨主攻领域:【渗透领域】【应急响应】 【Java】 【VulnHub靶场复现】【面试分析】 🎉点赞➕评论➕收藏 …

15-Linux部署HBase集群

Linux部署HBase集群 简介 HBase 是一种分布式、可扩展、支持海量数据存储的 NoSQL 数据库。 和Redis一样,HBase是一款KeyValue型存储的数据库。 不过和Redis设计方向不同 Redis设计为少量数据,超快检索HBase设计为海量数据,快速检索 HB…

运行Python文件时出现‘utf-8’code can‘t decode byte 如何解决?(如图)

如图 亦或者出现“SyntaxError: Non-UTF-8 code starting with \xbb ” 出现这种问题往往是编码格式导致的,我们可以在py文件中的第一行加入以下代码: # codingutf-8或者 # codinggdk优先使用gbk编码 解释一下常用的两种编码格式: utf-…

供应链管理(SCM):界面设计全面扫盲,得供应链者得天下

大家伙,我是大千UI工场,专注UI分享和项目接单,本期带来供应链系统的设计分享,欢迎大家关注、互动交流。 一、什么是SCM SCM系统是供应链管理(Supply Chain Management)系统的缩写。供应链管理是指协调和管…

CSS列表属性

CSS列表属性 列表相关的属性&#xff0c;可以作用在 ul、ol、li 元素上。 代码如下&#xff1a; <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><title>列表相关属性</title><style>ul {/* …

MySQL的一行数据是如何存储的?

目录 1.COMPACT 行格式长什么样&#xff1f; 例子1&#xff1a;用户设置了主键值&#xff0c;列都是not null的。(默认字符集是utf8mb4,在这种情况下&#xff0c;char(N)类型就不是定长的了&#xff09; 例子2&#xff1a;没有设置主键&#xff0c;也没有唯一索引&#xff0…

微信小程序-生命周期

页面生命周期 onLoad: 页面加载时触发的方法&#xff0c;在这个方法中可以进行页面初始化的操作&#xff0c;如获取数据、设置页面状态等。 onShow: 页面显示时触发的方法&#xff0c;在用户进入页面或从其他页面返回该页面时会调用此方法。可以在此方法中进行页面数据刷新、动…

浅谈马尔科夫链蒙特卡罗方法(MCMC)算法的理解

1.解决的问题 计算机怎么在任意给定的概率分布P上采样&#xff1f;首先可以想到把它拆成两步&#xff1a; &#xff08;1&#xff09;首先等概率的从采样区间里取一个待定样本x&#xff0c;并得到它的概率为p(x) &#xff08;2&#xff09;然后在均匀分布U[0,1]上取一个值&a…

基于主从模式的Reactor的仿muduo网络库

&#x1f307;个人主页&#xff1a;平凡的小苏 &#x1f4da;学习格言&#xff1a;命运给你一个低的起点&#xff0c;是想看你精彩的翻盘&#xff0c;而不是让你自甘堕落&#xff0c;脚下的路虽然难走&#xff0c;但我还能走&#xff0c;比起向阳而生&#xff0c;我更想尝试逆风…

分布式系统中常用的缓存方案

1. 引言 随着互联网应用的发展和规模的不断扩大&#xff0c;分布式系统中的缓存成为了提升性能和扩展性的重要手段之一。本文将介绍几种在分布式系统中常用的缓存方案&#xff0c;包括分布式内存缓存、分布式键值存储、分布式对象存储和缓存网关等。 1.1 缓存在分布式系统中的…

数据结构c版(3)——排序算法

本章我们来学习一下数据结构的排序算法&#xff01; 目录 1.排序的概念及其运用 1.1排序的概念 1.2 常见的排序算法 2.常见排序算法的实现 2.1 插入排序 2.1.1基本思想&#xff1a; 2.1.2直接插入排序&#xff1a; 2.1.3 希尔排序( 缩小增量排序 ) 2.2 选择排序 2.2…

WPS如何共享文件和文件夹

1 WPS共享单个文件 用WPS打开要分享的文件&#xff0c;点击右上角的“分享”键&#xff0c;选择上传到云端。 之后点击“创建并分享”&#xff0c;即可分享该文档。 2 WPS创建共享文件夹 2.1 如何共享文件夹 首先打开WPS&#xff0c;点击左上角的首页。在首页栏中&#…

Sqli-labs靶场第21、22关详解[Sqli-labs-less-21、22]自动化注入-SQLmap工具注入|sqlmap跑base64加密

Sqli-labs-Less-21、22 由于21/22雷同&#xff0c;都是需要登录后&#xff0c;注入点通过Cookie值进行测试&#xff0c;值base64加密 修改注入数据 选项&#xff1a;--tamperbase64encode #自动化注入-SQLmap工具注入 SQLmap用户手册&#xff1a;文档介绍 - sqlmap 用户手册 由…

SpringBoot+mybatisplus运行单元测试类报错unable to find a @SpringBootConfiguration

这个问题一般是因为启动类目录和测试类不一致&#xff0c;或者没有写使用SpringBootApplication注解的启动类。 1.如果没写启动类&#xff0c;请在与测试类同目录层级&#xff08;注意是在main/java下对应的目录&#xff0c;即测试类在test/java下的目录为com.xxx则启动类需要…