TapableHookPlugins

以极客时间《玩转Webpack》课程学习为主的记录笔记。

源码解读

  1. webpack的命令跟踪,从node_modules/webpack/bin/ 可以看到命令内容,webpack会查看是否下载安装了webpack-cli / webpack-command。
  2. 使用webpack-cli 解析命令行信息、安装使用到的依赖,启动webpack构建。

Hook和Tapable

  1. 从webpack的文件(/node_modules/webpack/lib/webpack.js)中可见:根绝传入的options,将生成多个不同的compiler实例。
  2. Compiler(/node_modules/webpack/lib/Compiler.js) / Complization
    Compiler、Complization继承了Tapable对象,内部实现了很多的hooks。
    常见Compiler Hooks
  • entryOption:在entry配置项处理过之后,执行一个插件。
  • emit:在生成资源到输出目录之前执行。
  • done:在编译完成时执行。

常见Compilation Hooks

  • buildModule:在模块构建之前执行。
  • optimize:在优化阶段开始时执行。
  • optimizeChunks:在块优化阶段时才执行。
  • afterSeal:完成封装之后执行。
Compiler

Compiler 对象代表了完整的 webpack 环境配置。这个对象在启动 webpack 时被一次性创建,并配置好所有可操作的设置,包括 options,loader 和 plugin。当在 webpack 环境中执行构建时,Compiler 对象负责处理所有事务。
当你运行 webpack 命令时,一个 Compiler 实例会被创建,并且在其生命周期中,它会对每个构建创建出一个新的 Compilation 对象。
Compiler 对象可以访问所有的 webpack 环境特定的功能,包括起始选项,构建的所有配置,以及注册的所有插件等。

Compilation

Compilation 对象包含了当前的模块资源、编译生成资源、变化的文件等。当 webpack 以开发模式运行时,每当检测到一个文件变化,就会创建一个新的 Compilation,意味着 Compilation 对象描述的是一次单一的版本构建和生成资源的过程。
在构建过程中,Compilation 实例会被用来收集从入口开始的所有依赖,对这些依赖进行处理、优化、分割等操作,并最终输出编译后的资源文件到文件系统。
总结一下,Compiler 是一个 webpack 构建过程中的全局上下文,而 Compilation 是对于单次构建的上下文抽象。开发者可以通过 hooks 挂钩到 CompilerCompilation 的生命周期事件中,以扩展和控制构建过程。例如,plugin 通过监听特定的事件,可以在某个特定的构建时刻介入构建流程,添加模块、改变输出资源等。

// 源码
class Compiler {/*** @param {string} context the compilation path* @param {WebpackOptions} options options*/constructor(context, options = /** @type {WebpackOptions} */ ({})) {this.hooks = Object.freeze({/** @type {SyncHook<[]>} */initialize: new SyncHook([]),/** @type {SyncBailHook<[Compilation], boolean | undefined>} */shouldEmit: new SyncBailHook(["compilation"]),/** @type {AsyncSeriesHook<[Stats]>} */done: new AsyncSeriesHook(["stats"]),/** @type {SyncHook<[Stats]>} */afterDone: new SyncHook(["stats"]),/** @type {AsyncSeriesHook<[]>} */additionalPass: new AsyncSeriesHook([]),/** @type {AsyncSeriesHook<[Compiler]>} */beforeRun: new AsyncSeriesHook(["compiler"]),/** @type {AsyncSeriesHook<[Compiler]>} */run: new AsyncSeriesHook(["compiler"]),/** @type {AsyncSeriesHook<[Compilation]>} */emit: new AsyncSeriesHook(["compilation"]),/** @type {AsyncSeriesHook<[string, AssetEmittedInfo]>} */assetEmitted: new AsyncSeriesHook(["file", "info"]),/** @type {AsyncSeriesHook<[Compilation]>} */afterEmit: new AsyncSeriesHook(["compilation"]),/** @type {SyncHook<[Compilation, CompilationParams]>} */thisCompilation: new SyncHook(["compilation", "params"]),/** @type {SyncHook<[Compilation, CompilationParams]>} */compilation: new SyncHook(["compilation", "params"]),/** @type {SyncHook<[NormalModuleFactory]>} */normalModuleFactory: new SyncHook(["normalModuleFactory"]),/** @type {SyncHook<[ContextModuleFactory]>}  */contextModuleFactory: new SyncHook(["contextModuleFactory"]),/** @type {AsyncSeriesHook<[CompilationParams]>} */beforeCompile: new AsyncSeriesHook(["params"]),/** @type {SyncHook<[CompilationParams]>} */compile: new SyncHook(["params"]),/** @type {AsyncParallelHook<[Compilation]>} */make: new AsyncParallelHook(["compilation"]),/** @type {AsyncParallelHook<[Compilation]>} */finishMake: new AsyncSeriesHook(["compilation"]),/** @type {AsyncSeriesHook<[Compilation]>} */afterCompile: new AsyncSeriesHook(["compilation"]),/** @type {AsyncSeriesHook<[]>} */readRecords: new AsyncSeriesHook([]),/** @type {AsyncSeriesHook<[]>} */emitRecords: new AsyncSeriesHook([]),/** @type {AsyncSeriesHook<[Compiler]>} */watchRun: new AsyncSeriesHook(["compiler"]),/** @type {SyncHook<[Error]>} */failed: new SyncHook(["error"]),/** @type {SyncHook<[string | null, number]>} */invalid: new SyncHook(["filename", "changeTime"]),/** @type {SyncHook<[]>} */watchClose: new SyncHook([]),/** @type {AsyncSeriesHook<[]>} */shutdown: new AsyncSeriesHook([]),/** @type {SyncBailHook<[string, string, any[]], true>} */infrastructureLog: new SyncBailHook(["origin", "type", "args"]),// TODO the following hooks are weirdly located here// TODO move them for webpack 5/** @type {SyncHook<[]>} */environment: new SyncHook([]),/** @type {SyncHook<[]>} */afterEnvironment: new SyncHook([]),/** @type {SyncHook<[Compiler]>} */afterPlugins: new SyncHook(["compiler"]),/** @type {SyncHook<[Compiler]>} */afterResolvers: new SyncHook(["compiler"]),/** @type {SyncBailHook<[string, Entry], boolean>} */entryOption: new SyncBailHook(["context", "entry"])});this.webpack = webpack;// ......}// ....
}
webpack可以理解为一种基于事件流的编程范例,一系列的插件运行。内部实现订阅和通知功能的模块就是Tapable。

Tapable

Tapable是一个类似于Nodejs EventEmiter的库,用于控制hooks的发布订阅功能的一个模块。

Tapable暴露了很多Hook,为插件提供挂载的钩子
const{
SyncHook, //同步钩子
SyncBailHook, //同步熔断钩子 当函数有任何返回值,就会在当前执行函数停止(遇到return将直接返回)
SyncWaterfallHook, //同步流水钩子 运行结果可以传递给下一个插件
SyncLoopHok, //同步循环钩子 监听函数返回true表示继续循环,返回undefine表示结束循环
AsyncParallelHook, //异步并发钩子
AsyncParallelBailHook, //异步并发熔断钩子
AsyncSeriesHook, //异步串行钩子
AsyncSeriesBailHook, //异步串行熔断钩子
AsyncSeriesWaterfallHook //异步串行流水钩子
} = require(“tapable”);

使用:const hook1 = new SyncHook(['argu1','argu2',])

使用方式:

Tabpack提供了同步&异步绑定钩子的方法,并且他们都有绑定事件和执行事件对
应的方法。

AsyncSync
绑定:tapAsync/tapPromise/tap绑定:tap
执行:callAsync/promise执行:call

简单用法:

const hook1 = new SyncHook(["arg1","arg2","arg3"]); //绑定事件流到webapck事件流
hook1.tap('hook1',(arg1,arg2,arg3)=>console.log(arg1,arg2,arg3))//1,2,3 //执行绑定的事件
hook1.call(1,2,3)

自测书写一个Plugin订阅构建实例(Compiler)中提供的hook,使用Compilation

Plugin的固定写法为:

  1. 暴露一个类。
  2. 类中存在webpack的调用入口:apply,webpack在初始化时,将主动执行Myplugin.apply,将compiler构建实例传入插件中,供挂载订阅hook节点使用。
  3. 看下方的实现代码,所有的发布hook都在compiler.hooks中,compiler.hooks.xxx就是发布的事件名称,订阅流程节点信息就是使用tap(同步)/ tapAsync(异步)绑定上事件名和方法。
// eg1:
// const Compiler = require('./Compiler')class MyPlugin{constructor() {}apply(compiler){compiler.hooks.calculateRoutes.tapPromise("calculateRoutes tapAsync", (source, target, routesList) => {return new Promise((resolve,reject)=>{setTimeout(()=>{console.log(`tapPromise to ${source} ${target} ${routesList}`)resolve();},1000)});});}
}const myPlugin = new MyPlugin();
// eg2:使用`emit`钩子在每次构建完成后输出一个文本文件
class MyExampleWebpackPlugin {// 插件必须具有apply方法apply(compiler) {// 确定在哪个阶段插入该插件compiler.hooks.emit.tapAsync('MyExampleWebpackPlugin', (compilation, callback) => {// 创建一份新的资源const mySource = '这是我的插件添加的资源内容';// 向Webpack输出列表中添加资源compilation.assets['my-plugin-output.txt'] = {source: function() {return mySource;},size: function() {return mySource.length;}};// 执行回调,表示插件处理完成callback();});}
}module.exports = MyExampleWebpackPlugin;

webpack流程

Chunk 生成算法
  1. webpack 先将 entry 中对应的 module 都生成一个新的chunk
  2. 遍历 module 的依赖列表,将依赖的 module 也加入到chunk中
  3. 如果一个依赖 module 是动态引入的模块,那么就会根据这个module创建一个新的 chunk,继续遍历依赖
  4. 重复上面的过程,直至得到所有的 chunks
AST

抽象语法树(abstract syntax tree 或者缩写为 AST),或者语法树(syntaxtree),是源代码的抽象语法结构的树状表现形式,这里特指编程语言的源代码。树上的每个节点都表示源代码中的一种结构。
在线demo: https://esprima.org/demo/parse.html

loader开发

一个最简单的loader栗子,loader主要用于"翻译代码变成JS引擎可识别的代码“

module.exports = function(source) {console.log ('loader a is executed');return source;
};
建议使用loader-runner来简化开发流程。

loader-runner可以提供一个独立的loader运行环境,不用下载webpack,有利于调试。

// 使用举例
// 新建一个命令文件 run-loader.js:const fs = require("fs");
const path = require("path");
const { runLoaders } = require("loader-runner");runLoaders({resource: "./demo.txt",		// 要翻译的对象文件loaders: [path.resolve(__dirname, "./loaders/rawloader")], 		// 当前loader在的地方readResource: fs.readFile.bind(fs), 	// 读取文件的方式},(err, result) => (err ? console.error(err) : console.log(result))
);
运行查看结果:
node run-loader.js

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

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

相关文章

浅谈jmeter性能测试步骤入门

一、Jmeter简介 1 概述 jmeter是一个软件&#xff0c;使负载测试或业绩为导向的业务&#xff08;功能&#xff09;测试不同的协议或技术。 它是 Apache 软件基金会的Stefano Mazzocchi JMeter 最初开发的。 它主要对 Apache JServ&#xff08;现在称为如 Apache Tomca…

相机图像质量研究(12)常见问题总结:光学结构对成像的影响--炫光

系列文章目录 相机图像质量研究(1)Camera成像流程介绍 相机图像质量研究(2)ISP专用平台调优介绍 相机图像质量研究(3)图像质量测试介绍 相机图像质量研究(4)常见问题总结&#xff1a;光学结构对成像的影响--焦距 相机图像质量研究(5)常见问题总结&#xff1a;光学结构对成…

C++ Qt框架开发 | 基于Qt框架开发实时成绩显示排序系统(3) 保存表格数据

对上两篇篇的工作C Qt框架开发| 基于Qt框架开发实时成绩显示排序系统&#xff08;1&#xff09;-CSDN博客和C Qt框架开发 | 基于Qt框架开发实时成绩显示排序系统&#xff08;2&#xff09;折线图显示-CSDN博客继续优化&#xff0c;增加一个保存按钮&#xff0c;用于保存成绩数据…

WebSocket原理详解

目录 1.引言 1.1.使用HTTP不断轮询 1.2.长轮询 2.websocket 2.1.概述 2.2.websocket建立过程 2.3.抓包分析 2.4.websocket的消息格式 3.使用场景 4.总结 1.引言 平时我们打开网页&#xff0c;比如购物网站某宝。都是点一下列表商品&#xff0c;跳转一下网页就到了商品…

【制作100个unity游戏之23】实现类似七日杀、森林一样的生存游戏17(附项目源码)

本节最终效果演示 文章目录 本节最终效果演示系列目录前言制作木板UI直接复制和工具一样的即可检查背包是否有指定数量的空插槽 源码完结 系列目录 前言 欢迎来到【制作100个Unity游戏】系列&#xff01;本系列将引导您一步步学习如何使用Unity开发各种类型的游戏。在这第23篇…

springboot184基于springboot的校园网上店铺的设计与实现

简介 【毕设源码推荐 javaweb 项目】基于springbootvue 的 适用于计算机类毕业设计&#xff0c;课程设计参考与学习用途。仅供学习参考&#xff0c; 不得用于商业或者非法用途&#xff0c;否则&#xff0c;一切后果请用户自负。 看运行截图看 第五章 第四章 获取资料方式 **项…

Go结构体深度探索:从基础到应用

在Go语言中&#xff0c;结构体是核心的数据组织工具&#xff0c;提供了灵活的手段来处理复杂数据。本文深入探讨了结构体的定义、类型、字面量表示和使用方法&#xff0c;旨在为读者呈现Go结构体的全面视角。通过结构体&#xff0c;开发者可以实现更加模块化、高效的代码设计。…

位运算+leetcode ( 2 )

题一&#xff1a;只出现一次的数字&#xff08;1&#xff09; 1.链接 136. 只出现一次的数字 - 力扣&#xff08;LeetCode&#xff09; 2.思想 借用位运算中异或操作符的特点&#xff0c;a^a0&#xff0c;0^aa先定义一个sum0就用一个循环来遍历这个数组&#xff0c;每次都进行…

基于JAVA的CRM客户管理系统 开源项目

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块三、系统设计3.1 用例设计3.2 E-R 图设计3.3 数据库设计3.3.1 客户表3.3.2 商品表3.3.3 客户跟踪表3.3.4 客户消费表3.3.5 系统角色表 四、系统展示五、核心代码5.1 查询客户5.2 新增客户跟踪记录5.3 新增客户消费订单5.4 查…

HandlerMapping之RequestMappingHandlerMapping作用详解

前言RequestMappingHandlerMapping RequestMappingRequestMapping的注册总结 前言 上回我们知道HandlerMapping是用来寻找Handler的&#xff0c;并不与Handler的类型或者实现绑定&#xff0c;而是根据需要定义的。 那么为什么要单独给RequestMapping实现一个HandlerMapping&…

《UE5_C++多人TPS完整教程》学习笔记12 ——《P13 加入游戏会话(Joining The Sessions)》

本文为B站系列教学视频 《UE5_C多人TPS完整教程》 —— 《P13 加入游戏会话&#xff08;Joining The Session&#xff09;》 的学习笔记&#xff0c;该系列教学视频为 Udemy 课程 《Unreal Engine 5 C Multiplayer Shooter》 的中文字幕翻译版&#xff0c;UP主&#xff08;也是…

操作系统基础:IO管理概述【上】

&#x1f308;个人主页&#xff1a;godspeed_lucip &#x1f525; 系列专栏&#xff1a;OS从基础到进阶 &#x1f3c6;&#x1f3c6;本文完整PDF源文件请翻阅至文章底部下载。&#x1f3c6;&#x1f3c6; &#x1f3ae;1 I/O设备的基本概念与分类&#x1f52b;1.1 总览&#x…