知海

Compilation 钩子

webpackjsorg-mainAPI 参考

Compilation 钩子

Compilation 模块被 Compiler 用来创建新的编译(或构建)。一个 compilation 实例可以访问所有模块及其依赖(其中大部分是循环引用)。它是对应用程序依赖图中所有模块的实际编译。在编译阶段,模块被加载、封装(sealed)、优化、分块(chunked)、哈希和恢复。

Compilation 类也继承自 Tapable,并提供了以下生命周期钩子。它们可以像 compiler 钩子一样被 tap:

js 复制代码
compilation.hooks.someHook.tap(/* ... */);

compiler 一样,根据钩子的类型,也可能可以使用 tapAsynctapPromise

W> 自 webpack 5 起,hooks 不再是可扩展的。请使用 WeakMap 来添加自定义钩子。

buildModule

SyncHook

在模块构建开始前触发,可用于修改模块。

  • 回调参数:module
js 复制代码
compilation.hooks.buildModule.tap(
  "SourceMapDevToolModuleOptionsPlugin",
  (module) => {
    module.useSourceMap = true;
  },
);

rebuildModule

SyncHook

在重建模块之前触发。

  • 回调参数:module

failedModule

SyncHook

当模块构建失败时运行。

  • 回调参数:module error

succeedModule

SyncHook

当模块成功构建后执行。

  • 回调参数:module

finishModules

AsyncSeriesHook

当所有模块都无错误地构建完成后调用。

  • 回调参数:modules

finishRebuildingModule

SyncHook

当模块重建后执行,无论成功或出错。

  • 回调参数:module

seal

SyncHook

当编译停止接受新模块时触发。

unseal

SyncHook

当编译开始接受新模块时触发。

optimizeDependencies

SyncBailHook

在依赖优化开始时触发。

  • 回调参数:modules

afterOptimizeDependencies

SyncHook

在依赖优化之后触发。

  • 回调参数:modules

afterChunks

SyncHook

afterChunks 钩子在创建 chunks 和模块图之后、优化它们之前被调用。这个钩子提供了一个检查、分析和必要时修改 chunk 图的机会。

这里有一个示例,展示如何使用 compilation.hooks.afterChunks 钩子。

  • 回调参数:chunks

optimize

SyncHook

在优化阶段开始时触发。

optimizeModules

SyncBailHook

在模块优化阶段开始时调用。插件可以 tap 这个钩子来对模块执行优化。

  • 回调参数:modules

afterOptimizeModules

SyncHook

在模块优化完成后调用。

  • 回调参数:modules

optimizeChunks

SyncBailHook

在 chunk 优化阶段开始时调用。插件可以 tap 这个钩子来对 chunk 执行优化。

  • 回调参数:chunks

afterOptimizeChunks

SyncHook

在 chunk 优化完成后触发。

  • 回调参数:chunks

optimizeTree

AsyncSeriesHook

在优化依赖树之前调用。插件可以 tap 这个钩子来执行依赖树优化。

  • 回调参数:chunks modules

afterOptimizeTree

SyncHook

在依赖树优化成功完成后调用。

  • 回调参数:chunks modules

optimizeChunkModules

SyncBailHook

在树优化之后、chunk 模块优化开始时调用。插件可以 tap 这个钩子来执行 chunk 模块的优化。

  • 回调参数:chunks modules

afterOptimizeChunkModules

SyncHook

在 chunk 模块优化成功完成后调用。

  • 回调参数:chunks modules

shouldRecord

SyncBailHook

调用以确定是否存储记录。返回任何 !== false 的值将阻止所有其他“记录”钩子被执行(recordrecordModulesrecordChunksrecordHash)。

reviveModules

SyncHook

从记录中恢复模块信息。

  • 回调参数:modules records

beforeModuleIds

SyncHook

在给每个模块分配 id 之前执行。

  • 回调参数:modules

moduleIds

SyncHook

调用以给每个模块分配 id

  • 回调参数:modules

optimizeModuleIds

SyncHook

在模块 id 优化开始时调用。

  • 回调参数:modules

afterOptimizeModuleIds

SyncHook

当模块 id 优化阶段完成后调用。

  • 回调参数:modules

reviveChunks

SyncHook

从记录中恢复 chunk 信息。

  • 回调参数:chunks records

beforeChunkIds

SyncHook

在给每个 chunk 分配 id 之前执行。

  • 回调参数:chunks

chunkIds

SyncHook

调用以给每个 chunk 分配 id

  • 回调参数:chunks

optimizeChunkIds

SyncHook

在 chunk id 优化阶段开始时调用。

  • 回调参数:chunks

afterOptimizeChunkIds

SyncHook

在 chunk id 优化完成后触发。

  • 回调参数:chunks

recordModules

SyncHook

将模块信息存储到记录中。只有当 shouldRecord 返回真值时才会触发。

  • 回调参数:modules records

recordChunks

SyncHook

将 chunk 信息存储到记录中。仅当 shouldRecord 返回真值时才会触发。

  • 回调参数:chunks records

beforeModuleHash

SyncHook

在模块被哈希之前调用。

afterModuleHash

syncHook

在模块被哈希之后调用。

beforeHash

SyncHook

在编译被哈希之前调用。

afterHash

SyncHook

在编译被哈希之后调用。

recordHash

SyncHook

将记录哈希的信息存储到 records 中。仅当 shouldRecord 返回真值时才会触发。

  • 回调参数:records

record

SyncHook

compilation 的信息存储到 records 中。仅当 shouldRecord 返回真值时才会触发。

  • 回调参数:compilation records

beforeModuleAssets

SyncHook

在创建模块资源之前执行。

additionalChunkAssets

SyncHook

W> additionalChunkAssets 已弃用(请改用 Compilation.hook.processAssets,并使用其中一个 Compilation.PROCESS_ASSETS_STAGE_* 作为 stage 选项)

为 chunk 创建额外的资源。

  • 回调参数:chunks

shouldGenerateChunkAssets

SyncBailHook

调用以确定是否生成 chunk 资源。返回任何 !== false 的值将允许生成 chunk 资源。

beforeChunkAssets

SyncHook

在创建 chunk 资源之前执行。

additionalAssets

AsyncSeriesHook

W> additionalAssets 已弃用(请改用 Compilation.hook.processAssets 钩子,并使用其中一个 Compilation.PROCESS_ASSETS_STAGE_* 作为 stage 选项)

为编译创建额外的资源。这个钩子可以用来例如下载图片:

js 复制代码
compilation.hooks.additionalAssets.tapAsync("MyPlugin", (callback) => {
  download("https://img.shields.io/npm/v/webpack.svg", (resp) => {
    if (resp.status === 200) {
      compilation.assets["webpack-version.svg"] = toAsset(resp);
      callback();
    } else {
      callback(
        new Error("[webpack-example-plugin] Unable to download the image"),
      );
    }
  });
});

optimizeChunkAssets

AsyncSeriesHook

W> optimizeChunkAssets 已弃用(请改用 Compilation.hook.processAssets,并使用其中一个 Compilation.PROCESS_ASSETS_STAGE_* 作为 stage 选项)

优化任何 chunk 资源。资源存储在 compilation.assets 中。一个 Chunk 有一个 files 属性,指向该 chunk 创建的所有文件。任何额外的 chunk 资源存储在 compilation.additionalChunkAssets 中。

  • 回调参数:chunks

下面是一个为每个 chunk 添加横幅(banner)的示例。

js 复制代码
compilation.hooks.optimizeChunkAssets.tapAsync(
  "MyPlugin",
  (chunks, callback) => {
    for (const chunk of chunks) {
      for (const file of chunk.files) {
        compilation.assets[file] = new ConcatSource(
          "/**Sweet Banner**/",
          "\n",
          compilation.assets[file],
        );
      }
    }

    callback();
  },
);

afterOptimizeChunkAssets

SyncHook

W> afterOptimizeChunkAssets 已弃用(请改用 Compilation.hook.processAssets,并使用其中一个 Compilation.PROCESS_ASSETS_STAGE_* 作为 stage 选项)

chunk 资源已经被优化。

  • 回调参数:chunks

下面是一个来自 @boopathi 的示例插件,它输出每个 chunk 中具体包含的内容。

js 复制代码
compilation.hooks.afterOptimizeChunkAssets.tap("MyPlugin", (chunks) => {
  for (const chunk of chunks) {
    console.log({
      id: chunk.id,
      name: chunk.name,
      includes: chunk.getModules().map((module) => module.request),
    });
  }
});

optimizeAssets

AsyncSeriesHook

W> optimizeAssets 已弃用(请改用 Compilation.hook.processAssets 钩子)

优化存储在 compilation.assets 中的所有资源。

  • 回调参数:assets

afterOptimizeAssets

SyncHook

W> afterOptimizeAssets 已弃用(请改用 Compilation.hook.afterProcessAssets 钩子)

资源已经被优化。

  • 回调参数:assets

processAssets

AsyncSeriesHook

资源处理。

钩子参数:

  • name: string — 插件的名称
  • stage: Stage — 要 tap 的阶段(参见下面的支持的阶段列表
  • additionalAssets?: true | (assets, [callback]) => (void | Promise<void>) — 用于额外资源的回调(参见下面的额外资源

回调参数:

  • assets: { [pathname: string]: Source } — 一个普通对象,其中键是资源的路径名,值是资源的数据,由 Source 表示。

示例:

js 复制代码
compilation.hooks.processAssets.tap(
  {
    name: "MyPlugin",
    stage: Compilation.PROCESS_ASSETS_STAGE_ADDITIONS, // 更多阶段见下文
  },
  (assets) => {
    console.log("List of assets and their sizes:");
    for (const [pathname, source] of Object.entries(assets)) {
      console.log(`— ${pathname}: ${source.size()} bytes`);
    }
  },
);

额外资源

除了 namestage,你还可以传递一个 additionalAssets 选项,它接受 true 或一个回调函数,该回调函数接收 assets 作为第一个参数:

  1. true — 对于插件稍后添加的资源,再次运行提供的回调。

    在这种模式下,回调将被多次调用:一次用于在指定阶段之前添加的资源,以及多次用于插件稍后(在当前或下一阶段)添加的资源。

    js 复制代码
    compilation.hooks.processAssets.tap(
      {
        name: "MyPlugin",
        stage: Compilation.PROCESS_ASSETS_STAGE_DEV_TOOLING,
        additionalAssets: true,
      },
      (assets) => {
        // 这个函数将被多次调用,每次处理一批资产
      },
    );
  2. (assets, [callback]) => (void | Promise<void>) — 针对插件稍后(在当前或下一阶段)添加的资源运行指定的回调。回调必须与所使用的 tap 方法的类型相匹配(例如,当与 tapPromise() 一起使用时,它应该返回一个 promise)。

    js 复制代码
    compilation.hooks.processAssets.tap(
      {
        name: "MyPlugin",
        stage: Compilation.PROCESS_ASSETS_STAGE_DEV_TOOLING,
        additionalAssets: (assets) => {
          // 这个函数可能会被多次调用,用于处理在后续阶段添加的资源
        },
      },
      (assets) => {
        // 这个函数将被调用一次,处理插件在先前阶段添加的资源
      },
    );

资源处理阶段列表

以下是支持的阶段列表(按处理顺序):

  • PROCESS_ASSETS_STAGE_ADDITIONAL — 向编译添加额外的资源。
  • PROCESS_ASSETS_STAGE_PRE_PROCESS — 资源的基本预处理。
  • PROCESS_ASSETS_STAGE_DERIVED — 从现有资源派生新资源。
  • PROCESS_ASSETS_STAGE_ADDITIONS — 向现有资源添加额外的部分,例如横幅或初始化代码。
  • PROCESS_ASSETS_STAGE_OPTIMIZE — 以一般方式优化现有资源。
  • PROCESS_ASSETS_STAGE_OPTIMIZE_COUNT — 优化现有资源的数量,例如通过合并它们。
  • PROCESS_ASSETS_STAGE_OPTIMIZE_COMPATIBILITY — 优化现有资源的兼容性,例如添加 polyfill 或厂商前缀。
  • PROCESS_ASSETS_STAGE_OPTIMIZE_SIZE — 优化现有资源的大小,例如通过最小化或省略空白。
  • PROCESS_ASSETS_STAGE_DEV_TOOLING — 向资源添加开发工具,例如通过提取 source map。
  • PROCESS_ASSETS_STAGE_OPTIMIZE_INLINE — 优化现有资源的数量,例如通过将资源内联到其他资源中。
  • PROCESS_ASSETS_STAGE_SUMMARIZE — 汇总现有资源的列表。
  • PROCESS_ASSETS_STAGE_OPTIMIZE_HASH — 优化资源的哈希,例如通过生成资源内容的真实哈希。
  • PROCESS_ASSETS_STAGE_OPTIMIZE_TRANSFER — 优化现有资源的传输,例如通过准备压缩(gzip)文件作为单独的资源。
  • PROCESS_ASSETS_STAGE_ANALYSE — 分析现有资源。
  • PROCESS_ASSETS_STAGE_REPORT — 为报告目的创建资源。

资源信息

此钩子不会自动提供“资源信息”元数据。如果需要,你将必须使用 compilation 实例和提供的资源路径名手动解析此元数据。这将在未来的 webpack 版本中得到改进。

示例:

js 复制代码
compilation.hooks.processAssets.tap(
  {
    /** … */
  },
  (assets) => {
    for (const [pathname, source] of Object.entries(assets)) {
      const assetInfo = compilation.assetsInfo.get(pathname);
      // @todo: 对 "pathname"、"source" 和 "assetInfo" 做一些处理
    }
  },
);

afterProcessAssets

SyncHook

processAssets 钩子无错误地完成后调用。

needAdditionalSeal

SyncBailHook

调用以确定编译是否需要取消封装以包含其他文件。

afterSeal

AsyncSeriesHook

needAdditionalSeal 之后立即执行。

chunkHash

SyncHook

为每个 chunk 触发以生成哈希。

  • 回调参数:chunk chunkHash

moduleAsset

SyncHook

当来自模块的资源被添加到编译时调用。

  • 回调参数:module filename

chunkAsset

SyncHook

当来自 chunk 的资源被添加到编译时触发。

  • 回调参数:chunk filename

assetPath

SyncWaterfallHook

调用以确定资源的路径。

  • 回调参数:path options

needAdditionalPass

SyncBailHook

调用以确定资源在发出后是否需要进一步处理。

childCompiler

SyncHook

在设置子编译器后执行。

  • 回调参数:childCompiler compilerName compilerIndex

normalModuleLoader

自 webpack v5 起,normalModuleLoader 钩子已被移除。现在要访问 loader,请使用 NormalModule.getCompilationHooks(compilation).loader

statsPreset

HookMap

这个 HookMap 类似于一系列动作,当使用预设时会触发。它接收一个 options 对象。当插件管理预设时,它应该小心地修改这个对象中的设置,而不是替换现有的设置。

  • 回调参数:options context

这是一个示例插件:

js 复制代码
compilation.hooks.statsPreset.for("my-preset").tap("MyPlugin", (options) => {
  if (options.all === undefined) options.all = true;
});

这个插件确保对于预设 'my-preset',如果 all 选项未定义,则默认为 true

statsNormalize

SyncHook

这个钩子用于将 options 对象转换为一致的格式,以便后续钩子可以轻松使用。它还确保缺失的选项被设置为默认值。

  • 回调参数:options context

这是一个示例插件:

js 复制代码
compilation.hooks.statsNormalize.tap("MyPlugin", (options) => {
  if (options.myOption === undefined) options.myOption = [];

  if (!Array.isArray(options.myOption)) options.myOptions = [options.myOptions];
});

在这个插件中,如果 myOption 缺失,则将其设置为空数组。此外,它确保 myOption 始终是一个数组,即使它最初被定义为单个值。

statsFactory

这个钩子提供了对特定选项的 StatsFactory 的访问。

  • 回调参数:statsFactory options

StatsFactory.hooks.extract

HookMap

  • 回调参数:object data context

data 包含类。object 是要添加属性的对象。context 提供上下文信息,例如路径上的类。

示例:

js 复制代码
compilation.hooks.statsFactory.tap("MyPlugin", (statsFactory, options) => {
  statsFactory.hooks.extract
    .for("compilation")
    .tap("MyPlugin", (object, compilation) => {
      object.customProperty = MyPlugin.getCustomValue(compilation);
    });
});

StatsFactory.hooks.result

HookMap

在每个级别上以结果调用。

  • 回调参数:result context

statsPrinter

这个钩子提供了对特定选项的 StatsPrinter 的访问。

  • 回调参数:statsPrinter options

StatsPrinter.hooks.print

HookMap

当应打印某个部分时调用此钩子。

  • 回调参数:object context

StatsPrinter.hooks.result

HookMap

当某个部分的结果字符串生成时调用此钩子。

  • 回调参数:result context

CssModulesPlugin.getCompilationHooks(compilation)

当启用 experiments.css 时,内部的 CssModulesPlugin 为插件作者提供了一组每个编译(per-compilation)的钩子。通过静态方法 getCompilationHooks(compilation) 访问它们:

js 复制代码
const webpack = require("webpack");

class MyCssPlugin {
  apply(compiler) {
    compiler.hooks.thisCompilation.tap("MyCssPlugin", (compilation) => {
      const hooks =
        webpack.css.CssModulesPlugin.getCompilationHooks(compilation);
      // 在这里 tap 钩子
    });
  }
}

W> 这些钩子仅在启用了 experiments.css 时注册,并且可能在 CSS 功能处于实验阶段时发生变化。

orderModules

SyncBailHook<[Chunk, Module[], Compilation], Module[] | undefined>

对于每种 CSS 源类型(CSS 导入和 CSS 模块),使用按完整模块名预排序的 chunk 模块调用一次。Tap 可以返回一个有序的 Module[] 来覆盖 webpack 默认的导入顺序拓扑排序,或者返回 undefined 以保持默认顺序。

当 webpack 的拓扑排序暴露出无法通过重构导入来解决的 Conflicting order between css ... 警告时,此钩子是推荐的逃生舱口(escape hatch)。返回预排序的数组(或任何确定性顺序)可以让构建选择稳定的顺序,而无需更改应用程序代码。

  • 回调参数:chunk modules compilation
js 复制代码
const webpack = require("webpack");

class CssOrderByPathPlugin {
  apply(compiler) {
    compiler.hooks.thisCompilation.tap(
      "CssOrderByPathPlugin",
      (compilation) => {
        const hooks =
          webpack.css.CssModulesPlugin.getCompilationHooks(compilation);

        // 模块到达时已按完整模块名预排序;原样返回以
        // 在重建时强制执行确定性的文件路径顺序,并
        // 避免冲突顺序警告。
        hooks.orderModules.tap(
          "CssOrderByPathPlugin",
          (_chunk, modules) => modules,
        );
      },
    );
  }
}

该钩子是一个 SyncBailHook,因此第一个返回非 undefined 值的 tap 生效。对于该调用,后续的 tap 将不会被调用。

HtmlModulesPlugin.getCompilationHooks(compilation)

当启用 experiments.html 时,内部的 HtmlModulesPlugin 为插件作者提供了一组每个编译(per-compilation)的钩子,用于注入和转换生成的 HTML 页面。通过静态方法 getCompilationHooks(compilation) 访问它们:

js 复制代码
const webpack = require("webpack");

class MyHtmlPlugin {
  apply(compiler) {
    compiler.hooks.thisCompilation.tap("MyHtmlPlugin", (compilation) => {
      const hooks =
        webpack.html.HtmlModulesPlugin.getCompilationHooks(compilation);
      // 在这里 tap 钩子
    });
  }
}

W> 这些钩子仅在启用了 experiments.html 时注册,并且可能在 HTML 功能处于实验阶段时发生变化。

injectTags

AsyncSeriesWaterfallHook<[HtmlTagDescriptor[], { outputName, html }]>

使用要注入到每个页面中的额外标签列表(最初为空)以及当前的 HTML 调用。推送描述符并返回列表;webpack 会序列化并放置它们。它在 output.html.csp 之前运行,因此注入的内联 <script> / <style> 标签会像页面自身的标签一样被哈希。

每个 HtmlTagDescriptor 接受:

  • tagstring):标签名,例如 'script' / 'link' / 'meta'
  • attrsobject):属性;true 渲染一个裸布尔属性,false / undefined 省略它。
  • childrenstring):内部内容(对于 <link> / <meta> 等空元素会被忽略)。
  • injectTo'head' | 'body' | 'head-prepend' | 'body-prepend'):放置位置,默认为 'head'
  • voidTagboolean):强制一个空元素(没有结束标签);省略时从标签名推断。
js 复制代码
hooks.injectTags.tap("MyHtmlPlugin", (tags) => {
  tags.push({
    tag: "meta",
    attrs: { name: "theme-color", content: "#2b3a42" },
  });
  return tags;
});

transformTags

AsyncSeriesHook<[HtmlMutableTag[], { outputName, html }]>

使用页面现有的 <script> / <link> / <style> / <meta> 标签(包括 webpack 自身的和任何注入的)作为可变描述符调用。修改 attrs(添加 nonce / data-* 属性,切换 deferasync,...),设置 remove: true,或更改 injectTo 以在 <head><body> 之间移动标签;webpack 会重写更改后的标签。改用 injectTags 添加新标签,并且不要重新排列数组本身。

transformHtml

AsyncSeriesWaterfallHook<[string, { outputName: string }]>

使用每个生成的页面的最终 HTML(所有占位符都已解析)在写入之前调用。返回(可能已转换的)HTML,例如对其进行压缩:

js 复制代码
hooks.transformHtml.tapPromise("MyHtmlPlugin", async (html, { outputName }) =>
  html.replace("<!-- banner -->", "..."),
);

htmlEmitted

AsyncSeriesHook<[{ outputName: string }]>

在每个页面的 HTML 资产最终确定后调用一次 — 一个发出后的通知。

帮助我们改进文档

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