Plugin API

插件是 webpack 生态系统中的关键部分,为社区提供了一种强大的方式来利用 webpack 的编译过程。插件能够钩住在每次编译过程中触发的关键事件。在整个过程中,插件将完全访问compiler,并在适用时访问当前的compilation

让我们从tapable实用程序开始,它提供了 webpack 插件接口的支柱。

Tapable

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

请参阅文档以了解更多信息。了解三种tap方法以及提供它们的钩子至关重要。扩展Tapable的对象(例如编译器)、它们提供的钩子以及每个钩子的类型(例如SyncHook)将被记录。

插件类型

根据使用的钩子和应用的tap方法,插件可以以不同的方式运行。这种工作方式与Tapable提供的钩子密切相关。每个编译器钩子都记录了底层的Tapable钩子,指示哪些tap方法可用。

因此,根据您tap到的事件,插件的运行方式可能不同。例如,当钩入compile阶段时,只能使用同步tap方法

compiler.hooks.compile.tap('MyPlugin', (params) => {
  console.log('Synchronously tapping the compile hook.');
});

但是,对于使用AsyncHookrun,我们可以使用tapAsynctapPromise(以及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.');
  }
);

故事的寓意是,有很多方法可以hookcompiler中,每种方法都允许你的插件按自己的方式运行。

自定义钩子

为了向编译提供自定义钩子,以便其他插件可以tap到其中,你需要执行以下操作

  1. 为编译钩子创建一个模块范围的WeakMap

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

    static getCompilationHooks(compilation: Compilation) : MyHooks {
      let hooks = compilationHooks.get(compilation);
      if(hooks === undefined) {
        compilationHooks.set(compilation, hooks = {
          custom: new SyncHook()
        });
      }
      return hooks;
    }
  3. 在你的插件中像下面这样调用钩子

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

    import MyPlugin from 'my-plugin';
    
    const hooks = MyPlugin.getCompilationHooks(compilation);
    
    hooks.custom.tap('OtherPlugin', (n, s) => {
      // magic
    });

同样,请参阅文档以了解有关不同钩子类及其工作原理的更多信息。

报告进度

插件可以通过ProgressPlugin报告进度,该插件默认情况下会将进度消息打印到 stderr。为了启用进度报告,在运行webpack CLI时传递一个--progress参数。

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

要报告进度,插件必须使用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 钩子和它们提供的参数的详细列表,请参阅 编译器钩子 部分。

7 位贡献者

thelarkinnpksjcee-cloudbyzykEugeneHlushkowizardofhogwartssnitin315