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