知海

插件 API

webpackjsorg-mainAPI 参考

插件 API

插件是 webpack 生态系统的关键组成部分,为社区提供了访问 webpack 编译过程的强大方式。插件能够钩入(hook) 每次编译过程中触发的关键事件。在整个过程中,插件都可以完全访问 compiler,并且在适用时访问当前的 compilation

提示: 想要了解编写插件的高级介绍,请从编写插件开始。

让我们从 tapable 工具开始,它提供了 webpack 插件接口的基础。

Tapable

这个小型库是 webpack 的核心工具,但也可以在其他地方使用,以提供类似的插件接口。webpack 中的许多对象都扩展了 Tapable 类。该类暴露了 taptapAsynctapPromise 方法,插件可以使用这些方法注入自定义构建步骤,这些步骤将在编译过程中被触发。

请参阅文档了解更多。理解这三种 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.");
});

然而,对于使用 AsyncHookrun,我们可以使用 tapAsynctapPromise(以及 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,你需要执行以下操作:

  1. 为编译钩子创建一个模块作用域的 WeakMap

    ts 复制代码
    const compilationHooks = new WeakMap<Compilation, MyHooks>();
    
    interface MyHooks {
      custom: SyncHook<[number, string]>;
    }
  2. 在你的插件上创建一个静态方法:

    ts 复制代码
    static getCompilationHooks(compilation: Compilation) : MyHooks {
      let hooks = compilationHooks.get(compilation);
      if(hooks === undefined) {
        compilationHooks.set(compilation, hooks = {
          custom: new SyncHook()
        });
      }
      return hooks;
    }
  3. 在你的插件中如下调用钩子:

    ts 复制代码
    const hooks = MyPlugin.getCompilationHooks(compilation);
    
    hooks.custom.call(1, "hello");
  4. 其他插件也可以访问你的自定义钩子:

    ts 复制代码
    import MyPlugin from "my-plugin";
    
    const hooks = MyPlugin.getCompilationHooks(compilation);
    
    hooks.custom.tap("OtherPlugin", (n, s) => {
      // magic
    });

同样,请参阅 tapable文档来了解更多不同的钩子类及其工作方式。

报告进度

插件可以通过 ProgressPlugin 报告进度,默认情况下它会将进度消息打印到 stderr。要启用进度报告,请在运行 webpack CLI 时传入 --progress 参数。

可以通过向 ProgressPluginreportProgress 函数传递不同参数来定制打印输出。

要报告进度,插件必须使用 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 钩子及其提供的参数的详细列表。

帮助我们改进文档

发现翻译问题或内容错误?请告诉我们。