插件 API
插件 API
插件是 webpack 生态系统的关键组成部分,为社区提供了访问 webpack 编译过程的强大方式。插件能够钩入(hook) 每次编译过程中触发的关键事件。在整个过程中,插件都可以完全访问 compiler,并且在适用时访问当前的 compilation。
提示: 想要了解编写插件的高级介绍,请从编写插件开始。
让我们从 tapable 工具开始,它提供了 webpack 插件接口的基础。
Tapable
这个小型库是 webpack 的核心工具,但也可以在其他地方使用,以提供类似的插件接口。webpack 中的许多对象都扩展了 Tapable 类。该类暴露了 tap、tapAsync 和 tapPromise 方法,插件可以使用这些方法注入自定义构建步骤,这些步骤将在编译过程中被触发。
请参阅文档了解更多。理解这三种 tap 方法以及提供它们的钩子至关重要。扩展 Tapable 的对象(例如 compiler)、它们提供的钩子以及每个钩子的类型(例如 SyncHook)都将被提及。
插件类型
根据所使用的钩子和应用的 tap 方法,插件可以以多种不同的方式工作。其工作方式与 Tapable 提供的钩子密切相关。编译器钩子 都注明了底层 Tapable 钩子,表示哪些 tap 方法可用。
因此,根据你 tap 进入的事件,插件可能会以不同的方式运行。例如,当钩入 compile 阶段时,只能使用同步的 tap 方法:
js
compiler.hooks.compile.tap("MyPlugin", (params) => {
console.log("Synchronously tapping the compile hook.");
});
然而,对于使用 AsyncHook 的 run,我们可以使用 tapAsync 或 tapPromise(以及 tap):
js
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) =>
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:tsconst compilationHooks = new WeakMap<Compilation, MyHooks>(); interface MyHooks { custom: SyncHook<[number, string]>; } -
在你的插件上创建一个静态方法:
tsstatic getCompilationHooks(compilation: Compilation) : MyHooks { let hooks = compilationHooks.get(compilation); if(hooks === undefined) { compilationHooks.set(compilation, hooks = { custom: new SyncHook() }); } return hooks; } -
在你的插件中如下调用钩子:
tsconst hooks = MyPlugin.getCompilationHooks(compilation); hooks.custom.call(1, "hello"); -
其他插件也可以访问你的自定义钩子:
tsimport MyPlugin from "my-plugin"; const hooks = MyPlugin.getCompilationHooks(compilation); hooks.custom.tap("OtherPlugin", (n, s) => { // magic });
同样,请参阅 tapable 的文档来了解更多不同的钩子类及其工作方式。
报告进度
插件可以通过 ProgressPlugin 报告进度,默认情况下它会将进度消息打印到 stderr。要启用进度报告,请在运行 webpack CLI 时传入 --progress 参数。
可以通过向 ProgressPlugin 的 reportProgress 函数传递不同参数来定制打印输出。
要报告进度,插件必须使用 context: true 选项来 tap 进入某个钩子:
js
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 函数可以传入以下参数调用:
js
reportProgress(percentage, ...args);
percentage:此参数未使用;相反,ProgressPlugin将根据当前钩子计算百分比。...args:任意数量的字符串,将被传递给ProgressPlugin处理器以报告给用户。
请注意,只有一部分 compiler 和 compilation 钩子支持 reportProgress 函数。请参阅 ProgressPlugin 获取完整列表。
日志
日志 API 自 webpack 4.37 发布以来可用。当在 stats configuration 中启用 logging 和/或启用了 infrastructure logging 时,插件可以记录消息,这些消息将按各自的记录器格式(stats、infrastructure)打印出来。
- 插件应优先使用
compilation.getLogger('PluginName')进行日志记录。这种日志存储在 Stats 中并相应格式化。用户可以过滤和导出它。 - 插件可以使用
compiler.getInfrastructureLogger('PluginName')进行日志记录。使用infrastructure日志不会存储在 Stats 中,因此不会被格式化。它通常直接记录到控制台/仪表板/GUI。用户可以过滤它。 - 插件可以使用特定的回退逻辑来检测日志支持:
compilation.getLogger ? compilation.getLogger('PluginName') : console,以便在使用的 webpack 旧版本不支持compilation对象上的getLogger方法时提供回退。
下一步
请参阅编译器钩子部分,了解所有可用的 compiler 钩子及其提供的参数的详细列表。
帮助我们改进文档
发现翻译问题或内容错误?请告诉我们。
