Plugin进阶
写在前面(题外话)
其实webpack大体上的学习我早就完成了,只是人懒,不想去写博客。我感觉很多人应该都像我一样吧,懒得去写文档。不过一想到连loader
都写了,有始有终干脆把plugin
也写了好了。
介绍
首先来看一下官方给Plugin的定义。
插件是 webpack 生态系统的重要组成部分,为社区用户提供了一种强大方式来直接触及 webpack 的编译过程(compilation process)。插件能够 钩入(hook) 到在每个编译(compilation)中触发的所有关键事件。在编译的每一步,插件都具备完全访问
compiler
对象的能力,如果情况合适,还可以访问当前compilation
对象。
根据介绍可以看得出来,plugin
的编写逃不开两个对象compiler
和compilation
。而这两个对象都是继承自Tapable
类。而它对外暴露了 tap
、tapAsync
和 tapPromise
等方法, 插件可以使用这些方法通过不同hooks
绑定进入对应的构建进程中,并在构建过程中触发。
Hook Types
上面提到了Tapable
会提供不同的hooks
来使用tap
、tapAsync
和 tapPromise
来绑定进构建进程。
既然如此,来进一步了解一下,hooks
有哪些
根据执行顺序区分
Basic
会按照顺序执行每个方法,但不关心方法的返回值Waterfall
会按照顺序执行每个方法,但会接受上一个函数的返回值,也会传递返回值给下一个函数Bail
当函数返回了非undefined
值时,会直接阻断接下来的函数运行Loop
当函数返回了非undefined
值时,会重新开始该顺序的函数执行
根据执行性质区分
Sync
同步执行,只能通过tap
绑定AsyncSeries
异步串联执行,能通过tap
、tapAsync
和.tapPromise
绑定AsyncParallel
异步并联执行,能通过tap
、tapAsync
和.tapPromise
绑定
为什么要分成两个大块呢,因为这两种区分方式可以进行两两组合,成为一种新的hook
。所以Tapable一共给我们提供了以下这么多的hook
:
const {
SyncHook,
SyncBailHook,
SyncWaterfallHook,
SyncLoopHook,
AsyncParallelHook,
AsyncParallelBailHook,
AsyncSeriesHook,
AsyncSeriesBailHook,
AsyncSeriesWaterfallHook
} = require("tapable");
compiler & compilation
Compiler
模块是 webpack 的支柱引擎,它通过 CLI 或 Node API 传递的所有选项,创建出一个Compiler
实例。它扩展(extend)自Tapable
类,以便注册和调用插件。大多数面向用户的插件首,会先在Compiler
上注册。
Compilation
模块会被Compiler
用来创建新的编译(或新的构建)。compilation
实例能够访问所有的模块和它们的依赖(大部分是循环依赖)。它会对应用程序的依赖图中所有模块进行字面上的编译(literal compilation)。在编译阶段,模块会被加载(loaded)、封存(sealed)、优化(optimized)、分块(chunked)、哈希(hashed)和重新创建(restored)。
compiler
是构建之初就已经创建,并且贯穿webpack整个生命周期,即每次运行webpack(或运行 npm run serve 和 npm run build
等) 构建时实例,且只有一个。
compilation: compilation
是到准备编译模块时,才会创建compilation
对象,是 compile - make
阶段主要使用的对象。
这两个对象中的hooks
如下:
compiler 钩子 | webpack 中文网 (webpackjs.com)
compilation 钩子 | webpack 中文网 (webpackjs.com)
编写plugin
class myPlugin {
constructor (options) {
// 获取插件配置项
this.filename = options;
}
apply(compiler) {
compiler.hooks.compile.tap('MyPlugin', (params) => {
console.log('以同步方式触及 compile 钩子。');
});
compiler.hooks.run.tapAsync('MyPlugin', (source, target, routesList, callback) => {
console.log('以异步方式触及运行钩子。');
callback();
})
compiler.hooks.run.tapPromise('MyPlugin', (source, target, routesList) => {
return new Promise((resolve) => setTimeout(resolve, 1000)).then(() => {
console.log('以异步的方式触发具有延迟操作的钩子。');
});
compiler.hooks.run.tapPromise('MyPlugin', async (source, target, routesList) => {
await new Promise((resolve) => setTimeout(resolve, 1000));
console.log('以异步的方式触发具有延迟操作的钩子。');
});
}
}
module.exports = myPlugin