知海

插件 API

webpackjsorg-mainAPI 参考

插件 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,你需要执行以下操作:

  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 参数。

可以通过向 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 钩子及其提供的参数的详细列表。

帮助我们改进文档

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