HtmlWebpackPlugin
前言官方文档
关于HtmlWebpackPlugin插件的时间,相信不少童鞋应该都是比较熟悉的了,在一些现成的脚手架(像vue-cli、react-cli等开源三方脚手架)中,都被集成进去成为其核心成员脚手架模块,负责将结果目标内容文件给输出出来, 0⃣ 配置的方式来快速开发这个业务项目, 😕 但是,在实际的项目开发过程中,避无可避地需要针对现有的采用脚手架搭建起来的项目进行定制化的配置,来满足不断变更的业务需求,提升编码的效率,因此,很有必要来对所使用的相关插件(HtmlWebpackPlugin)进行深入的了解!关于官方的解释是:HtmlWebpackPlugin 简化了 HTML 文件的创建,以便为你的 webpack 包提供服务。这对于那些文件名中包含哈希值,并且哈希值会随着每次编译而改变的 webpack 包特别有用。你可以让该插件为你生成一个 HTML 文件,使用 lodash 模板提供模板,或者使用你自己的 loader。👾 这里提及的使用默认的lodash模版来生成Html文件,还可以使用自己的loader,比如像handlerbars-loader、html-l ...
webpack中如何加载loader的
前言webpack官方loader介绍
关于webpack中的Loader,平时项目中可能比较少用到,一般的脚手架程序都自动集成相关的loader,但是如果我们能够将不同的loader给搭配起来,形成项目自身特色的loader集合体的话,在开发/编码过程中可以提升不少的效率,而且也让程序可以 🈶 更健壮的稳定性!😕 那么什么是Loader?如何使用Loader?Loader的加载执行过程是怎样的?一般都有哪些常用的Loader?如何自定义Loader来辅助工作?
什么是Loader?
loader用于对模块的源代码进行转换,可以让我们在使用import或者’load’模块时预处理文件,通过对loader的定义与使用,可以让我们在处理不同的文件格式时,采用不同的文件解析协议,比如像*.hbs、*.vue等文件,通过采用对应的loader来将其解析为不同的内容!比如将typescript转换为普通的js,活着将内联图像转换为data URL,甚至允许我们直接在js模块中直接导入css文件。😕 关键在于预处理,使得我们的所有的模块(文件)加载时都可以先处理转换一下,采用一些官方的公 ...
webpack中的模块管理
前言
本文主要着重介绍关于webpack中的模块是如何管理的,其实在网络上已经也有不少的文章关于ES6、CommonJS、AMD的相关介绍了,本文主要侧重于这个模块管理的使用方式!三者都是对模块进行依赖管理的目的,实现同步/异步加载的目标!
ESM
ESM主要包含 🈶 2⃣ 个关键词:import和export!
export
export 语句用于从模块中导出实时绑定的函数、对象或原始值,以便其他程序可以通过 import 语句使用它们。被导出的绑定值依然可以在本地进行修改。在使用 import 进行导入时,这些绑定值只能被导入模块所读取,但在 export 导出模块中对这些绑定值进行修改,所修改的值也会实时地更新。存在 🈶 种方式的导出:
命名导出(每个模块包含任意数量)
默认导出(每个模块包含一个)
语法规则:
123456789101112131415161718192021222324252627// 导出单个特性export let name1, name2, …, nameN; // also var, constexport let name1 = …, ...
DefinePlugin在webpack中是如何使用的
前言DefinePlugin官网链接
允许在编译时将你代码中的变量替换为其他值或表达式,它并不是变量赋值,而是在编译期间进行字符串的精准匹配与替换!可以理解为,我们在webpack打包的项目中,通过在配置文件(webpack.config.js)中使用插件DefinePlugin,并传递给该插件对应的变量/表达式,都可以在项目中的*.js文件中访问到这些变量/表达式的定义,而无需重复地在所有的文件中进行二次定义!这意味着我们可以定义全局开关等控制,来对代码进行区别式运行的操作!
用法123456789// webpack.config.jsmodule.exports = { plugins: [ new webpack.DefinePlugin({ // 此处是相关的变量/表达式的配置 a: 999 }) ]}
关于该插件中每个健值对可以 🈶 👇 的定义方式:
如果该值为字符串,它将被作为代码片段来使用;
如果该值不是字符串,则将被转换成字符串(包括函数方法);
如果值是一个对象,则它所有的键将使用相同方法定义(也就是遍历key来替换); ...
AssetModulesPlugin.js
我是谁
webpack中与assets资源进行交互的模块,逐渐通过对NormalModuleFacotry.hooks.createParser、NoemalModuleFactory.createGenerator、Compilation.hooks.renderManifest等几个钩子容器函数的监听!
干预的钩子函数
通过提供的干预的钩子函数,创建对应的Parser以及Generator对象,来处理webpack中的默认的对asset资源加载的统一配置,也就是对配置的实现!
1、NormalModuleFactory.hooks.createParser
针对不同类型的createParser创建对应的Parser对象,主要由以下 👇 几种类型的asset:
asset类型
对应创建的Parser
asset
AssetParser
asset/inline
AssetParser
asset/resource
AssetParser
asset/source
AssetSourceParser
2、NormalModuleFactory.ho ...
webpack中的代码生成插件
前言
作为webpack中的代码生成器插件,主要负责将对应的chunk生成对应的结果字符串内容!在开始具体解析之前,首先,现看一下 👇 2⃣ 个最简单的结果生成情况对比!
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657 (() => { // webpackBootstrap var __webpack_modules__ = ({/***/ "./src/module1.js":/*!************************!*\ !*** ./src/module1.js ***! \************************//***/ ((module) => {module.exports = function test() { console.info("我是来自于模块一中的test方法");};/***/ }) });/************************ ...
Module与其家族们
前言
Module,作为webpack中的基本模块单元,继承于DependenciesBlock,使其具备缓存其他模块而形成的依赖关系,也就是说,webpack中的关于模块的定义声明皆来自于此!我们所定义的一个个文档都是一个个的模块,模块之间允许互相依赖,而依赖在程序中的表现形式,则是以DependenciesBlock中的Dependency[]数组来存储的
DependenciesBlock
作为抽象的模块基类,允许添加/删除依赖,就像是Array数组般一样!
通过其成员属性dependencies(Dependency数组类型),来维护引用依赖的其他模块,主要用与同步加载的其他模块;
通过其成员属性block(AsyncDependenciesBlock数组类型),来维护引用依赖的其他模块,采用异步分割代码加载的方式来加载的其他模块;
parent属性,指向父模块;
Dependency
作为依赖模块形成的基本单元,是一个模块关系的基本单元,可以认为是依赖的代码位置描述对象,其成员属性/方法主要有:
loc: 是一个DepencencyLocation对象,代表依赖形成 ...
EntryOptionPlugin
前言
作为实际开始编译的入口,该插件由webpack的核心默认插件自动加载,无需额外通过配置文件引入!
入口的开始
通过对该插件的代码阅读后发现,该插件主要针对两个钩子容器函数设置监听动作
compiler.hooks.compilation
compiler.hooks.make
🌠 下面我们关键分析一下这个compiler.hooks.make触发时,做了什么动作!
一切编译动作的源头
在compiler.hooks.make方法触发时,在该插件中通过调用compilation.addEntry来从入口文件加载相关的依赖文件!一切回到了 Compilation.js
webpack中的AsyncQueue
前言
在阅读到Compilation的代码执行过程时,发现有一个隐藏的异步队列执行者,😕 这里为啥要单独整一篇文章来阐述这个异步队列执行者呢?我觉得应该是在于它的一个比较独特的设计理念打动了我,下面就来具体分析一下并尝试来使用这个异步队列的执行者!添加完成一个元素后,自动执行一个对应的回调动作,并能够实现与父AsyncQueue对象共享的“线程队列”资源
如何使用?1234567891011121314151617const AsyncQueue = require("../../lib/util/AsyncQueue");let child = new AsyncQueue({ name: "test", parallelism: 2, processor: () => { console.info("我是来自于processor的执行动作"); }});child.add({}, err => { console.info("我是来自于添加失败后的回调!");});child.add({}, err => { console.info("我是 ...
DllReferencePlugin
前言
官方链接
DllPlugin一般与DllReferencePlugin配套使用, 😕 为什么这么说呢?
DllPlugin一般用来在webpack中创建一个单独的dll-only-bundles,同时生成一个名为manifest.json文件;
DllReferencePlugin则是会通过引用上述第一步所创建出来的manifest.json文件,来告知如何加载模块;一般在webpack.config.js中使用,此插件会把 dll-only-bundles 引用到需要的预编译的依赖中,用某种方法实现了拆分 bundles,同时还大幅度提升了构建的速度,从字面意思上称之为:”动态链接引用插件+对应json配置使用”
dll动态链接库
**动态链接库(Dynamic-link library,简写为DLL)**,是在winsows系统中实现共享函数库的一种方式,一般就是把一些经常回共享的代码制作成DLL文档,当可执行文件调用到DLL文档中的函数的时候,才会将DLL加载到内存中,当程序有需求时函数才进行过滤,可大幅降低存储!
👽 DLL其实就是一个缓存文档,使用的时候,不 ...
webpack中的Compilation
前言官方链接
webpack中真正的”编译器执行者”,Compilation实例能够访问所有的模块和它们的依赖(大部分是循环依赖)。 它会对应用程序的依赖图中所有模块, 进行字面上的编译(literal compilation)。 在编译阶段,模块会被加载(load)、封存(seal)、优化(optimize)、 分块(chunk)、哈希(hash)和重新创建(restore)。👇 是关于Compilation对象的组成结构:
Compilation的工作流程是怎样的呢?
👽 其实Compilation自身并没有做任何的动作,而是一堆的插件它们负责来实现的!因为Compiler创建完这个Compilation对象之后,就没有针对这个Compilation对象进行调用其相关的API了,而是将Compilation以及params作为参数,来触发Complier.hooks.thisCompilation
首先,针对一个上面最简单的webpack.config.js进行一个针对thisCompilation事件的一个插件执行队列分析,从上图中我们可以对应整理对应的执行队列: ...
NormalModuleReplacementPlugin
前言官方链接
NormalModuleReplacementPlugin 允许我们将与 resourceRegExp 匹配的资源替换为 newResource。如果 newResource 是相对的,则它是相对于先前资源解析的。如果 newResource 是一个函数,它应该覆盖所提供资源的请求属性。☝ 官方的描述有点绕,简而言之,就是模块的在加载之前进行一个替换,这个替换按照正则表达式resourceRegExp所定义的表达式来实现替换,可以实现到类似于cross-env工具的效果:通过自定义变量的方式来达到根据不同的变量来尽性替换的目的!
如何使用(官方例子)比如我们 🈶 👇 的一个webpack.config.js配置文件:
1234567891011121314module.exports = function(env){ var appTarget = env.APP_TARGET || 'VERSION_A'; return { // ... 此处隐藏其他的配置 plugins: [ new NormalModuleReplacement( ...
NormalModuleFactory的工作过程
前言
官方链接单从其名字上来看,可以简单理解为创建module的工厂! 而实际在阅读完它的源码的时候发现,其实它就真的是用来创建module的工厂!Compiler使用NormalModuleFactory模块生成各类模块,从entry入口开始,此模块回分解每个请求,解析配置文件webpack.config.js内容来查找进一步的请求,然后通过分解所有的请求以及解析新的文件来爬去全部的文件,最后,每个依赖项都会成为一个模块实例!!😕 那么NormalModuleFactory是如何从entry开始来解析的呢?解析过程是怎样的?
☝ 从这里我们可以清楚地知晓NormalModuleFactory是被Compiler所创建的,通过传递一个配置对象,来创建的NormalModuleFactory对象的,那么这个配置对象中的属性又是怎么一回事呢?👉 首先,webpack中的文件都是一个个的模块,也就是说要将文件“转变”为模块,需要fs文件模块,通过读取文件来进行的源文件的读取,但是node中仅仅只能够识别到普通的js/json/node等文件格式,像图片资源等这些,又如何被识别到呢?我们 ...
SyncModuleIdsPlugin
前言
维护着一个data属性,主要负责记录与读取之前的构建记录,关于这个构建记录,官方的描述是:webpack 的 “records” 记录 - 即「用于存储跨多次构建(across multiple builds)的模块标识符」的数据片段,一般可以通过与webpack.config.js配合配置,直接讲对应的文件给输出出来,可以使用此文件来跟踪在每次构建之间的模块变化。只要简单的设置一下路径,就可以生成这个 JSON 文件
如何使用
配合webpack.config.js,在构建打包的时候输出对应的文件,便于调试调整优化打包配置文件,如下图所示:
监听的钩子生命周期有
Compiler.hooks.readRecords: 从之前的缓存(data,map对象)中读取缓存过的构建记录;
Compiler.hooks.emitRecords: 写入构建缓存记录
Compiler.hooks.thisCompilation:
webpack插件-NodeEnvironmentPlugin
前言
webpack内部的自执行的插件,主要负责提供infrastructureLogger属性到compiler对象上,并创建对应的文件属性到compiler对象上,然后监听到compiler.hooks.beforeRun钩子容器中,将过程日志怼到标准输入中!用于基础设施水平的日志选项
webpack插件-ProgressPlugin
前言
ProgressPlugin提供了一种自定义编译过程中进度报告方式的方法!
官方链接
使用方式
一般地,我们可以通过创建一个ProgressPlugin对象并传递允许的可选参数,来实现自定义输出的目的!
123456789// webpack.config.jsconst {ProgressPlugin} = require("webpack");module.exports = { plugins: [ new ProgressPlugin({ //... 参数说明见下表 }) ]};
☝ 加入了对应的插件配置后,则有对应的 👇 的日志输出
参数名称
类型
描述
activeModules
boolean
显示活动模块计数和一个正在进行的活动模块消息
dependencies
boolean
显示依赖的数量
dependenciesCount
boolean
显示的最小依赖数
entries
boolean
显示入口数量
handler
HandlerFunction
在每一步的过程中被调用
modules
boo ...
webpack中的Compiler
前言
上文中我们所提及到的webpack创建了一个Compiler对象,由它来进行相关的打包任务动作的启动等,而且作为所有的插件所共同访问的到的一个编译器对象,😕 那么,这个Compiler是什么呢??它做了哪些事情呢?
Compiler的组成
Compiler是什么?执行过程是怎样的?
Compiler 模块是 webpack 的主要引擎,它通过 CLI 或者 Node API 传递的所有选项创建出一个 compilation 实例。 它扩展(extends)自 Tapable 类,用来注册和调用插件。 大多数面向用户的插件会首先在 Compiler 上注册。首先,先来看一下 👇 的一副Compile的run方法的执行过程所触发的钩子函数,以及都有哪些插件在对应的钩子容器中添加了各自的钩子函数动作:☝ 我们可以清楚地了解到run方法的执行过程, 👇 将一一来分析每一个节点过程
1、触发beforeRun方法
在开始执行一次构建之前调用,compiler.run 方法开始执行后立刻进行调用
描述
值
传递参数
Compiler对象
调用方式
AsyncSer ...
webpack学习计划
前言
现在基本上每一个前端童鞋都与webpack在工作学习上都有所依赖,不管是以前还是未来,webpack都将占据非常重要的地位,而且都会与每一位前端童鞋息息相关,那么我是否可以像之前阅读其他的源码一样,来通过阅读webpack的源码,一步一步跟着代码来读懂这个webpack呢?起初我也是这么做的,然后我放弃了,其中的代码量之多,而且又超级复杂,错综复杂我觉得已经不能够用来形容这个webpack了,😫 不可能完全通过单纯的硬着头皮来阅读代码来理解,因此,我觉得需要整理一套方便自己来理解这个webpack的一整套方案来理解整个webpack框架,本次学习基于V5.75.0版本的!
webpack的组成结构
通过简单的通读webpack的相关源代码,自己整理了关于webpack的几个比较重要模块,如下图所示:关于几个模块,我觉得有点像战场上的战斗一般,指挥官(Compiler)根据上级指令(webpack.config.js配置)安排好一切之后,下达指令给士兵(Compilation),士兵接收命令进行作战(编译/打包),在作战的过程中,拥有一系列的装备(Plugin)的加持,使得士兵 ...
tapable使用与源码分析
前言
在刚接触webpack的时候,仅仅只是懂得了关于如何通过简单的配置,来告知webpack如何进行一个打包工作,但是,对于webpack中具体是如何工作的,却全然不晓得,因此,觉得很有必要来对其中的执行过程进行一个深入的学习,在学习的过程中,又遇到了一个看着一头雾水的代码this.hooks.compiler.XXX系列方法,通过相关的资料查阅才知道,原来webpack中的compiler的相关的勾子函数的实现,都是由这个tabable来实现,因此,编写此文章来记录一下学习的过程,加深对tapable的理解,也可以在自己后续的框架中来使用到这个框架!
tapable是什么?
在经过相关的学习文档以及使用的方式得出的结果,其实tapable它是一个类似于connect中间件的一个框架,但是它又比connect框架又有更多的额外的特殊属性,正是由于这个特殊的属性,采用多次注册一次执行的方式,使得wepback能够实现复杂的多变的”类似生命周期”的相关函数方法:
容器的思维,在hooks对象中所定义的每一个属性都是一个容器,每个容器都可以有无限多的钩子函数;
容器中的钩子函数的执行 ...