插件是 webpack 生态系统中的关键部分,为社区提供了一种强大的方式来利用 webpack 的编译过程。插件能够钩住在每次编译过程中触发的关键事件。在整个过程中,插件将完全访问compiler
,并在适用时访问当前的compilation
。
让我们从tapable
实用程序开始,它提供了 webpack 插件接口的支柱。
这个小型库是 webpack 中的核心实用程序,但也可以在其他地方使用以提供类似的插件接口。webpack 中的许多对象都扩展了Tapable
类。该类公开了tap
、tapAsync
和tapPromise
方法,插件可以使用这些方法来注入自定义构建步骤,这些步骤将在整个编译过程中触发。
请参阅文档以了解更多信息。了解三种tap
方法以及提供它们的钩子至关重要。扩展Tapable
的对象(例如编译器)、它们提供的钩子以及每个钩子的类型(例如SyncHook
)将被记录。
根据使用的钩子和应用的tap
方法,插件可以以不同的方式运行。这种工作方式与Tapable
提供的钩子密切相关。每个编译器钩子都记录了底层的Tapable
钩子,指示哪些tap
方法可用。
因此,根据您tap
到的事件,插件的运行方式可能不同。例如,当钩入compile
阶段时,只能使用同步tap
方法
compiler.hooks.compile.tap('MyPlugin', (params) => {
console.log('Synchronously tapping the compile hook.');
});
但是,对于使用AsyncHook
的run
,我们可以使用tapAsync
或tapPromise
(以及tap
)
compiler.hooks.run.tapAsync(
'MyPlugin',
(source, target, routesList, callback) => {
console.log('Asynchronously tapping the run hook.');
callback();
}
);
compiler.hooks.run.tapPromise('MyPlugin', (source, target, routesList) => {
return new Promise((resolve) => setTimeout(resolve, 1000)).then(() => {
console.log('Asynchronously tapping the run hook with a delay.');
});
});
compiler.hooks.run.tapPromise(
'MyPlugin',
async (source, target, routesList) => {
await new Promise((resolve) => setTimeout(resolve, 1000));
console.log('Asynchronously tapping the run hook with a delay.');
}
);
故事的寓意是,有很多方法可以hook
到compiler
中,每种方法都允许你的插件按自己的方式运行。
为了向编译提供自定义钩子,以便其他插件可以tap
到其中,你需要执行以下操作
为编译钩子创建一个模块范围的WeakMap
const compilationHooks = new WeakMap<Compilation, MyHooks>();
interface MyHooks {
custom: SyncHook<[number, string]>;
}
在你的插件上创建一个静态方法
static getCompilationHooks(compilation: Compilation) : MyHooks {
let hooks = compilationHooks.get(compilation);
if(hooks === undefined) {
compilationHooks.set(compilation, hooks = {
custom: new SyncHook()
});
}
return hooks;
}
在你的插件中像下面这样调用钩子
const hooks = MyPlugin.getCompilationHooks(compilation);
hooks.custom.call(1, 'hello');
其他插件也可以访问你的自定义钩子
import MyPlugin from 'my-plugin';
const hooks = MyPlugin.getCompilationHooks(compilation);
hooks.custom.tap('OtherPlugin', (n, s) => {
// magic
});
同样,请参阅文档以了解有关不同钩子类及其工作原理的更多信息。
插件可以通过ProgressPlugin
报告进度,该插件默认情况下会将进度消息打印到 stderr。为了启用进度报告,在运行webpack CLI时传递一个--progress
参数。
可以通过向ProgressPlugin
的reportProgress
函数传递不同的参数来自定义打印的输出。
要报告进度,插件必须使用context: true
选项tap
到钩子中
compiler.hooks.emit.tapAsync(
{
name: 'MyPlugin',
context: true,
},
(context, compiler, callback) => {
const reportProgress = context && context.reportProgress;
if (reportProgress) reportProgress(0.95, 'Starting work');
setTimeout(() => {
if (reportProgress) reportProgress(0.95, 'Done work');
callback();
}, 1000);
}
);
reportProgress
函数可以使用以下参数调用
reportProgress(percentage, ...args);
percentage
:此参数未使用;相反,ProgressPlugin
将根据当前钩子计算百分比。...args
:任意数量的字符串,这些字符串将传递给ProgressPlugin
处理程序,以便向用户报告。请注意,只有编译器和编译钩子的一部分支持reportProgress
函数。有关完整列表,请参阅ProgressPlugin
。
日志记录 API 自 webpack 4.37 版本起可用。当在 stats 配置
中启用 logging
或/或启用 基础设施日志记录
时,插件可能会记录消息,这些消息将以相应的日志记录器格式(统计信息、基础设施)打印出来。
compilation.getLogger('PluginName')
进行日志记录。这种类型的日志记录存储在统计信息中并进行相应格式化。用户可以对其进行过滤和导出。compiler.getInfrastructureLogger('PluginName')
进行日志记录。使用 infrastructure
日志记录不会存储在统计信息中,因此不会进行格式化。它通常直接记录到控制台/仪表板/GUI。用户可以对其进行过滤。compilation.getLogger ? compilation.getLogger('PluginName') : console
,以便在使用不支持 compilation
对象上的 getLogger
方法的旧版 webpack 版本时提供回退。有关所有可用 compiler
钩子和它们提供的参数的详细列表,请参阅 编译器钩子 部分。