可打印

简介

多种接口可用于自定义编译过程。某些功能在接口之间重叠,例如,配置选项可能通过 CLI 标志可用,而其他功能则仅通过单个接口存在。以下高级信息应能帮助你入门。

CLI

命令行界面 (CLI) 用于配置和与你的构建交互。它在早期原型设计和性能分析中特别有用。在大多数情况下,CLI 用于使用配置文件和一些标志(例如 --env)来启动进程。

了解更多关于 CLI 的信息!

模块

在使用 webpack 处理模块时,了解所支持的不同模块语法(特别是方法变量)非常重要。

了解更多关于模块的信息!

Node

虽然大多数用户可以使用 CLI 和配置文件,但通过 Node 接口可以实现对编译更细粒度的控制。这包括传递多个配置、以编程方式运行或监视以及收集统计信息。

了解更多关于 Node API 的信息!

加载器

加载器是对模块源代码应用的转换。它们被写成函数,接受源代码作为参数,并返回应用了转换的新版本代码。

了解更多关于加载器的信息!

插件

插件接口允许用户直接参与编译过程。插件可以在生命周期钩子(在编译过程中的不同点运行)上注册处理程序。当每个钩子执行时,插件将完全访问编译的当前状态。

了解更多关于插件的信息!

命令行界面

为了正确使用和更方便地分发此配置,webpack 可以通过 webpack.config.js 进行配置。发送到 CLI 的任何参数都将映射到配置文件中的相应参数。

如果你尚未安装 webpack 和 CLI,请阅读安装指南

命令

webpack-cli 提供了各种命令,使使用 webpack 变得更容易。默认情况下,webpack 附带以下命令:

命令用法描述
构建build|bundle|b [入口...] [选项]运行 webpack(默认命令,可省略)。
配置测试configtest|t [配置路径]验证 webpack 配置。
帮助help|h [命令] [选项]显示命令和选项的帮助信息。
信息info|i [选项]输出有关你系统的信息。
服务serve|server|s [选项]运行 webpack-dev-server
版本version|v [命令...]输出 webpackwebpack-cliwebpack-dev-server 的版本号。
监视watch|w [入口...] [选项]运行 webpack 并监视文件更改。

构建

运行 webpack(默认命令,可省略)。

npx webpack build [options]

示例

npx webpack build --config ./webpack.config.js --stats verbose

初始化

用于使用 create-new-webpack-app 初始化新的 webpack 项目。

npx create-new-webpack-app [generation-path] [options]

示例

npx create-new-webpack-app ./my-app --force --template=default

别名

npx create-new-webpack-app init ./my-app --force --template=default

生成路径

生成配置的位置。默认为 process.cwd()

选项

-t, --template

字符串 = 'default'

要生成的模板名称。

-f, --force

布尔值

无需提问即可生成项目。启用后,每个问题的默认答案都将被使用。

支持的模板

  • --template=default - 带有基本配置的默认模板。
  • --template=react - 带有 React 配置的模板。
  • --template=vue - 带有 Vue 配置的模板。
  • --template=svelte - 带有 Svelte 配置的模板。`

加载器

脚手架加载器。

npx create-new-webpack-app loader [output-path] [options]

示例

npx create-new-webpack-app loader ./my-loader --template=default

输出路径

输出目录的路径,例如 ./loader-name

选项

-t, --template

字符串 = 'default'

模板类型。

插件

脚手架插件。

npx create-new-webpack-app plugin [output-path] [options]

示例

npx create-new-webpack-app plugin ./my-plugin --template=default

输出路径

输出目录的路径,例如 ./plugin-name

选项

-t, --template

字符串 = 'default'

模板类型。

信息

输出有关你系统的信息。

npx webpack info [options]

示例

npx webpack info --output json --addition-package postcss

信息选项

-a, --additional-package

字符串

向输出中添加额外的包。

示例

npx webpack info --additional-package postcss

-o, --output

字符串 : 'json' | 'markdown'

以指定格式获取输出。

示例

npx webpack info --output markdown

配置测试

验证 webpack 配置。

npx webpack configtest [config-path]

示例

npx webpack configtest ./webpack.config.js

配置路径

你的 webpack 配置文件路径。默认为 ./webpack.config.js

服务

运行 webpack 开发服务器。

npx webpack serve [options]

示例

npx webpack serve --static --open

监视

运行 webpack 并监视文件更改。

npx webpack watch [options]

示例

npx webpack watch --mode development

标志

默认情况下,webpack 附带以下标志:

标志 / 别名类型描述
--entry字符串数组应用程序的入口点,例如 ./src/main.js
--config, -c字符串数组提供 webpack 配置文件路径,例如 ./webpack.config.js
--config-name字符串数组要使用的配置名称
--name字符串配置名称。在加载多个配置时使用
--color布尔值在控制台启用颜色
--merge, -m布尔值使用 webpack-merge 合并两个或多个配置
--env字符串数组当配置是函数时传递给配置的环境
--config-node-env字符串process.env.NODE_ENV 设置为指定值
--progress布尔值,字符串在构建期间打印编译进度
--help布尔值输出支持的标志和命令列表
--output-path, -o字符串webpack 生成文件的输出位置,例如 ./dist
--target, -t字符串数组设置构建目标
--watch, -w布尔值监视文件更改
--watch-options-stdin布尔值当 stdin 流结束时停止监视
--devtool, -d字符串控制是否以及如何生成源映射。
--json, -j布尔值,字符串将结果打印为 JSON 或将其存储到文件中
--mode字符串定义要传递给 webpack 的模式
--version, -v布尔值获取当前版本
--stats布尔值,字符串它指示 webpack 如何处理统计数据
--disable-interpret布尔值禁用 interpret 加载配置文件。
--fail-on-warnings布尔值当 webpack 发出警告时,以非零退出代码停止 webpack-cli 进程
--analyze布尔值它调用 webpack-bundle-analyzer 插件以获取打包信息
--extends, -e字符串数组扩展现有配置

否定标志

标志描述
--no-color禁用控制台上的任何颜色
--no-hot如果你通过配置启用了热重载,则禁用它
--no-stats禁用 webpack 发出的任何编译统计信息
--no-watch不监视文件更改
--no-devtool不生成源映射
--no-watch-options-stdin当 stdin 流结束时,不停止监视

核心标志

从 CLI v4 和 webpack v5 开始,CLI 从 webpack 核心导入整个配置 schema,以允许从命令行调整几乎所有配置选项。

以下是 webpack v5 与 CLI v4 支持的所有核心标志列表 - 链接

例如,如果你想在项目中启用性能提示,你可以在配置中使用选项,使用核心标志你可以这样做 -

npx webpack --performance-hints warning

用法

使用配置文件

npx webpack [--config webpack.config.js]

有关配置文件的选项,请参阅配置

不使用配置文件

npx webpack --entry <entry> --output-path <output-path>

示例

npx webpack --entry ./first.js --entry ./second.js --output-path /build

entry

作为构建项目入口点的文件名或一组命名文件名。你可以传递多个入口(每个入口都在启动时加载)。以下是通过 CLI 指定入口文件的多种方式 -

npx webpack --entry-reset ./first-entry.js
npx webpack --entry-reset --entry ./first-entry.js
npx webpack --entry-reset ./first-entry.js ./other-entry.js
npx webpack --entry-reset --entry ./first-entry.js ./other-entry.js

output-path

打包文件要保存的路径。它将映射到配置选项 output.path

示例

如果你的项目结构如下 -

.
├── dist
├── index.html
└── src
    ├── index.js
    ├── index2.js
    └── others.js
npx webpack ./src/index.js --output-path dist

这将以 index.js 作为入口打包你的源代码,输出的打包文件路径将是 dist

asset main.js 142 bytes [compared for emit] [minimized] (name: main)
./src/index.js 30 bytes [built] [code generated]
./src/others.js 1 bytes [built] [code generated]
webpack 5.1.0 compiled successfully in 187 ms
npx webpack ./src/index.js ./src/others2.js --output-path dist/

这将以这两个文件作为单独的入口点来形成打包。

asset main.js 142 bytes [compared for emit] [minimized] (name: main)
./src/index.js 30 bytes [built] [code generated]
./src/others2.js 1 bytes [built] [code generated]
./src/others.js 1 bytes [built] [code generated]
webpack 5.1.0 compiled successfully in 198 ms

默认配置

CLI 会在你的项目路径中查找一些默认配置,以下是 CLI 拾取的配置文件。

这是按优先级递增的查找顺序:

示例 - 配置文件查找顺序为 .webpack/webpackfile > .webpack/webpack.config.js > webpack.config.js

'webpack.config',
'.webpack/webpack.config',
'.webpack/webpackfile',

常用选项

帮助

列出 cli 上可用的基本命令和标志

webpack help [命令] [选项]webpack [命令] --help 都是获取帮助的有效方式

npx webpack --help

# or

npx webpack help

列出 cli 支持的所有命令和标志

npx webpack --help=verbose

查看特定命令或选项的帮助

npx webpack help --mode

版本

显示已安装包和子包的版本

要检查你正在使用的 webpackwebpack-cli 版本,请运行命令:

npx webpack --version

# or

npx webpack version

这将输出以下结果:

webpack 5.31.2
webpack-cli 4.6.0

如果你已安装 webpack-dev-server,它也将输出其版本。

webpack 5.31.2
webpack-cli 4.6.0
webpack-dev-server 3.11.2

要检查任何 webpack-cli 子包(如 @webpack-cli/info)的版本,请运行类似于以下命令:

npx webpack info --version

这将输出以下结果:

@webpack-cli/info 1.2.3
webpack 5.31.2
webpack-cli 4.6.0
webpack-dev-server 3.11.2

config

使用配置文件构建源

指定不同于 webpack.config.js配置文件webpack.config.js 是默认配置文件之一。

npx webpack --config example.config.js

config-name

如果你的配置文件导出了多个配置,你可以使用 --config-name 来指定要运行哪个配置。

考虑以下 webpack.config.js

module.exports = [
  {
    output: {
      filename: './dist-first.js',
    },
    name: 'first',
    entry: './src/first.js',
    mode: 'development',
  },
  {
    output: {
      filename: './dist-second.js',
    },
    name: 'second',
    entry: './src/second.js',
    mode: 'development',
  },
  {
    output: {
      filename: './dist-third.js',
    },
    name: 'third',
    entry: './src/third.js',
    mode: 'none',
    stats: 'verbose',
  },
];

只运行 second 配置

npx webpack --config-name second

你也可以传递多个值

npx webpack --config-name first --config-name second

合并

你可以借助 --merge 合并两个或多个不同的 webpack 配置

npx webpack --config ./first.js --config ./second.js --merge

扩展

webpack-cli v5.1.0+

你可以借助 --extends 扩展现有 webpack 配置

npx webpack --extends ./base.webpack.config.js

扩展配置中了解更多信息。

JSON

将 webpack 结果打印为 JSON

npx webpack --json

如果你想将统计数据存储为 JSON 而不是打印出来,你可以使用 -

npx webpack --json stats.json

在所有其他情况下,webpack 会打印一组统计数据,显示打包、块和时间细节。使用此选项,输出可以是 JSON 对象。此响应可被 webpack 的分析工具、chrisbateman 的webpack-visualizer 或 th0r 的webpack-bundle-analyzer 接受。分析工具将接收 JSON 并以图形形式提供构建的所有详细信息。

环境选项

当 webpack 配置导出一个函数时,可以向其传递一个“环境”。

env

npx webpack --env production    # env.production = true

--env 参数接受多个值

调用结果环境
npx webpack --env prod{ prod: true }
npx webpack --env prod --env min{ prod: true, min: true }
npx webpack --env platform=app --env production{ platform: "app", production: true }
npx webpack --env foo=bar=app{ foo: "bar=app"}
npx webpack --env app.platform="staging" --env app.name="test"{ app: { platform: "staging", name: "test" }

除了上面展示的自定义 env 之外,env 下还有一些内置变量可以在你的 webpack 配置中使用:

环境变量描述
WEBPACK_SERVE如果正在使用 serve|server|s,则为 true
WEBPACK_BUILD如果正在使用 build|bundle|b,则为 true
WEBPACK_WATCH如果正在使用 --watch|watch|w,则为 true

请注意,你无法在打包代码中访问这些内置环境变量。

module.exports = (env, argv) => {
  return {
    mode: env.WEBPACK_SERVE ? 'development' : 'production',
  };
};

node-env

你可以使用 --node-env 选项来设置 process.env.NODE_ENV,它对用户代码和 webpack 配置都可用。

npx webpack --node-env production   # process.env.NODE_ENV = 'production'

config-node-env

webpack-cli v6.0.0+

--node-env 的别名,用于设置 process.env.NODE_ENV

npx webpack --config-node-env production   # process.env.NODE_ENV = 'production'

当配置中未指定 mode 选项时,你可以使用 --config-node-env 选项来设置 mode。例如,使用 --config-node-env production 将把 process.env.NODE_ENVmode 都设置为 'production'

如果你的配置导出一个函数,--config-node-env 的值将在函数返回后分配给 mode。这意味着 mode 将在函数参数(envargv)中不可用。但是,--config-node-env 的值可以在函数内部作为 argv.nodeEnv 访问,并可以相应地使用。

module.exports = (env, argv) => {
  console.log(argv.defineProcessEnvNodeEnv); // 'production' if --config-node-env production is used
  return {
    // your configuration
  };
};

配置选项

参数解释输入类型默认值
--config配置文件路径字符串数组默认配置
--config-name要使用的配置名称字符串数组-
--env当配置是函数时,传递给配置的环境字符串数组-

分析打包

你也可以使用 webpack-bundle-analyzer 来分析 webpack 发出的输出打包。你可以使用 --analyze 标志通过 CLI 调用它。

npx webpack --analyze

进度

要检查任何 webpack 编译的进度,你可以使用 --progress 标志。

npx webpack --progress

要收集进度步骤的配置文件数据,你可以将 profile 作为值传递给 --progress 标志。

npx webpack --progress=profile

将 CLI 参数传递给 Node.js

要将参数直接传递给 Node.js 进程,你可以使用 NODE_OPTIONS 选项。

例如,将 Node.js 进程的内存限制增加到 4 GB:

NODE_OPTIONS="--max-old-space-size=4096" webpack

此外,你还可以向 Node.js 进程传递多个选项。

NODE_OPTIONS="--max-old-space-size=4096 -r /path/to/preload/file.js" webpack

退出代码及其含义

退出代码描述
0成功
1来自 webpack 的错误
2配置/选项问题或内部错误

CLI 环境变量

环境变量描述
WEBPACK_CLI_SKIP_IMPORT_LOCAL当为 true 时,它将跳过使用本地的 webpack-cli 实例。
WEBPACK_CLI_FORCE_LOAD_ESM_CONFIG当为 true 时,它将强制加载 ESM 配置。
WEBPACK_PACKAGE在 CLI 中使用自定义 webpack 版本。
WEBPACK_DEV_SERVER_PACKAGE在 CLI 中使用自定义 webpack-dev-server 版本。
WEBPACK_CLI_HELP_WIDTH为帮助输出使用自定义宽度。
WEBPACK_CLI_FORCE_LOAD_ESM_CONFIG=true npx webpack --config ./webpack.config.esm

WEBPACK_PACKAGE

在 CLI 中使用自定义 webpack 版本。考虑你的 package.json 中的以下内容:

{
  "webpack": "^4.0.0",
  "webpack-5": "npm:webpack@^5.32.0",
  "webpack-cli": "^4.5.0"
}

要使用 webpack v4.0.0

npx webpack

要使用 webpack v5.32.0

WEBPACK_PACKAGE=webpack-5 npx webpack

故障排除

TypeError [ERR_UNKNOWN_FILE_EXTENSION]:./webpack.config.ts 的未知文件扩展名“.ts”

在使用 TypeScript 中的原生 ESM(即 package.json 中的 type: "module")时,你可能会遇到此错误。

webpack-cli 支持 CommonJSESM 格式的配置,它首先尝试使用 require() 加载配置,一旦以错误代码 'ERR_REQUIRE_ESM'(针对这种情况的特殊代码)失败,它将尝试使用 import() 加载配置。然而,在未启用加载器钩子的情况下,import() 方法将无法与 ts-node 一起工作(详见 TypeStrong/ts-node#1007)。

要修复上述错误,请使用以下命令:

NODE_OPTIONS="--loader ts-node/esm" npx webpack --entry ./src/index.js --mode production

有关更多信息,请参阅我们关于使用 TypeScript 编写 webpack 配置的文档。

Node 接口

Webpack 提供了一个 Node.js API,可以直接在 Node.js 运行时中使用。

Node.js API 在你需要自定义构建或开发过程的场景中非常有用,因为所有的报告和错误处理都必须手动完成,而 webpack 只负责编译部分。因此,stats 配置选项在 webpack() 调用中将不会有任何效果。

安装

要开始使用 webpack Node.js API,如果你尚未安装 webpack,请先安装它:

npm install --save-dev webpack

然后在你的 Node.js 脚本中引入 webpack 模块:

const webpack = require('webpack');

或者如果你更喜欢 ES2015:

import webpack from 'webpack';

webpack()

导入的 webpack 函数接收一个 webpack 配置对象,如果提供了回调函数,则运行 webpack 编译器。

const webpack = require('webpack');

webpack({}, (err, stats) => {
  if (err || stats.hasErrors()) {
    // ...
  }
  // Done processing
});

编译器实例

如果你不向 webpack 运行器函数传递回调,它将返回一个 webpack Compiler 实例。此实例可用于手动触发 webpack 运行器,或使其构建并监视更改,这与CLI 非常相似。Compiler 实例提供以下方法:

  • .run(callback)
  • .watch(watchOptions, handler)

通常,只创建一个主 Compiler 实例,尽管可以创建子编译器以委托特定任务。Compiler 最终是一个执行最基本功能以维持生命周期运行的函数。它将所有加载、打包和写入工作委托给已注册的插件。

Compiler 实例上的 hooks 属性用于将插件注册到 Compiler 生命周期中的任何钩子事件。WebpackOptionsDefaulterWebpackOptionsApply 工具由 webpack 用于配置其 Compiler 实例,并使用所有内置插件。

然后使用 run 方法启动所有编译工作。完成后,执行给定 callback 函数。统计信息和错误的最终日志记录应在此 callback 函数中完成。

运行

Compiler 实例上调用 run 方法与上面提到的快速运行方法非常相似:

const webpack = require('webpack');

const compiler = webpack({
  // ...
});

compiler.run((err, stats) => {
  // ...

  compiler.close((closeErr) => {
    // ...
  });
});

监听

调用 watch 方法会触发 webpack 运行器,然后监视更改(很像 CLI:webpack --watch),一旦 webpack 检测到更改,就会再次运行。返回一个 Watching 实例。

watch(watchOptions, callback);
const webpack = require('webpack');

const compiler = webpack({
  // ...
});

const watching = compiler.watch(
  {
    // Example
    aggregateTimeout: 300,
    poll: undefined,
  },
  (err, stats) => {
    // Print watch/build result here...
    console.log(stats);
  }
);

Watching 选项在这里有详细介绍。

关闭 Watching

watch 方法返回一个 Watching 实例,该实例公开 .close(callback) 方法。调用此方法将结束监视:

watching.close((closeErr) => {
  console.log('Watching Ended.');
});

使 Watching 失效

使用 watching.invalidate,你可以手动使当前编译轮次失效,而无需停止监视进程。

watching.invalidate();

统计对象

作为 webpack() 回调的第二个参数传递的 stats 对象是关于代码编译过程的良好信息来源。它包括:

  • 错误和警告(如果有)
  • 时间
  • 模块和块信息

webpack CLI 使用此信息在控制台中显示格式良好的输出。

stats 对象公开以下方法:

stats.hasErrors()

可用于检查编译时是否存在错误。返回 truefalse

stats.hasWarnings()

可用于检查编译时是否存在警告。返回 truefalse

stats.toJson(options)

将编译信息作为 JSON 对象返回。options 可以是字符串(预设)或对象,以实现更精细的控制。

stats.toJson('minimal');
stats.toJson({
  assets: false,
  hash: true,
});

所有可用选项和预设都在统计文档中描述。

这是此函数输出的一个示例

stats.toString(options)

返回编译信息的格式化字符串(类似于 CLI 输出)。

选项与 stats.toJson(options) 相同,但有一个附加项:

stats.toString({
  // Add console colors
  colors: true,
});

这是 stats.toString() 用法的示例:

const webpack = require('webpack');

webpack(
  {
    // ...
  },
  (err, stats) => {
    if (err) {
      console.error(err);
      return;
    }

    console.log(
      stats.toString({
        chunks: false, // Makes the build much quieter
        colors: true, // Shows colors in the console
      })
    );
  }
);

多编译器

MultiCompiler 模块允许 webpack 在单独的编译器中运行多个配置。如果 webpack 的 NodeJS API 中的 options 参数是一个选项数组,webpack 将应用单独的编译器并在所有编译器执行完毕后调用 callback

var webpack = require('webpack');

webpack(
  [
    { entry: './index1.js', output: { filename: 'bundle1.js' } },
    { entry: './index2.js', output: { filename: 'bundle2.js' } },
  ],
  (err, stats) => {
    process.stdout.write(stats.toString() + '\n');
  }
);

错误处理

为了良好的错误处理,你需要考虑以下三种类型的错误:

  • 致命的 webpack 错误(配置错误等)
  • 编译错误(缺少模块、语法错误等)
  • 编译警告

这是实现所有这些的一个示例:

const webpack = require('webpack');

webpack(
  {
    // ...
  },
  (err, stats) => {
    if (err) {
      console.error(err.stack || err);
      if (err.details) {
        console.error(err.details);
      }
      return;
    }

    const info = stats.toJson();

    if (stats.hasErrors()) {
      console.error(info.errors);
    }

    if (stats.hasWarnings()) {
      console.warn(info.warnings);
    }

    // Log result...
  }
);

自定义文件系统

默认情况下,webpack 使用普通文件系统读写文件到磁盘。但是,可以通过使用不同类型的文件系统(内存、webDAV 等)来改变输入或输出行为。为此,可以更改 inputFileSystemoutputFileSystem。例如,你可以用 memfs 替换默认的 outputFileSystem,将文件写入内存而不是磁盘:

const { createFsFromVolume, Volume } = require('memfs');
const webpack = require('webpack');

const fs = createFsFromVolume(new Volume());
const compiler = webpack({
  /* options */
});

compiler.outputFileSystem = fs;
compiler.run((err, stats) => {
  // Read the output later:
  const content = fs.readFileSync('...');
  compiler.close((closeErr) => {
    // ...
  });
});

请注意,webpack-dev-middleware(被 webpack-dev-server 和许多其他包使用)正是通过这种方式神秘地隐藏你的文件,但仍然将它们提供给浏览器!

统计数据

当使用 webpack 编译源代码时,用户可以生成一个包含模块统计信息的 JSON 文件。这些统计信息可用于分析应用程序的依赖图,以及优化编译速度。该文件通常通过以下 CLI 命令生成:

npx webpack --profile --json=compilation-stats.json

--json=compilation-stats.json 标志指示 webpack 应发出包含依赖图和各种其他构建信息的 compilation-stats.json 文件。通常,还会添加 --profile 标志,以便在每个modules 对象中添加一个 profile 部分,其中包含模块特定的编译统计信息。

结构

输出 JSON 文件的顶层结构相当简单,但也存在一些嵌套数据结构。每个嵌套结构在下面都有专门的部分,以使本文档更易于理解。请注意,你可以点击下面顶层结构中的链接,跳转到相关部分和文档。

{
  "version": "5.9.0", // Version of webpack used for the compilation
  "hash": "11593e3b3ac85436984a", // Compilation specific hash
  "time": 2469, // Compilation time in milliseconds
  "publicPath": "auto",
  "outputPath": "/", // path to webpack output directory
  "assetsByChunkName": {
    // Chunk name to emitted asset(s) mapping
    "main": ["web.js?h=11593e3b3ac85436984a"],
    "named-chunk": ["named-chunk.web.js"],
    "other-chunk": ["other-chunk.js", "other-chunk.css"]
  },
  "assets": [
    // A list of asset objects
  ],
  "chunks": [
    // A list of chunk objects
  ],
  "modules": [
    // A list of module objects
  ],
  "entryPoints": {
    // A list of entry objects
  },
  "errors": [
    // A list of error objects
  ],
  "errorsCount": 0, // number of errors
  "warnings": [
    // A list of warning objects
  ],
  "warningsCount": 0 // nummber of warnings
}

资产对象

每个 assets 对象表示从编译发出的一个 output 文件。它们都遵循类似的结构:

{
  "chunkNames": [], // The chunks this asset contains
  "chunks": [10, 6], // The chunk IDs this asset contains
  "comparedForEmit": false, // Indicates whether or not the asset was compared with the same file on the output file system
  "emitted": true, // Indicates whether or not the asset made it to the `output` directory
  "name": "10.web.js", // The `output` filename
  "size": 1058, // The size of the file in bytes
  "info": {
    "immutable": true, // A flag telling whether the asset can be long term cached (contains a hash)
    "size": 1058, // The size in bytes, only becomes available after asset has been emitted
    "development": true, // A flag telling whether the asset is only used for development and doesn't count towards user-facing assets
    "hotModuleReplacement": true, // A flag telling whether the asset ships data for updating an existing application (HMR)
    "sourceFilename": "originalfile.js", // sourceFilename when asset was created from a source file (potentially transformed)
    "javascriptModule": true // true, when asset is javascript and an ESM
  }
}

块对象

每个 chunks 对象表示一组模块,称为。每个对象遵循以下结构:

{
  "entry": true, // Indicates whether or not the chunk contains the webpack runtime
  "files": [
    // An array of filename strings that contain this chunk
  ],
  "filteredModules": 0, // See the description in the [top-level structure](#structure) above
  "id": 0, // The ID of this chunk
  "initial": true, // Indicates whether this chunk is loaded on initial page load or [on demand](/guides/lazy-loading)
  "modules": [
    // A list of [module objects](#module-objects)
    "web.js?h=11593e3b3ac85436984a"
  ],
  "names": [
    // An list of chunk names contained within this chunk
  ],
  "origins": [
    // See the description below...
  ],
  "parents": [], // Parent chunk IDs
  "rendered": true, // Indicates whether or not the chunk went through Code Generation
  "size": 188057 // Chunk size in bytes
}

chunks 对象还将包含一个 origins 列表,描述给定块的来源。每个 origins 对象遵循以下模式:

{
  "loc": "", // Lines of code that generated this chunk
  "module": "(webpack)\\test\\browsertest\\lib\\index.web.js", // Path to the module
  "moduleId": 0, // The ID of the module
  "moduleIdentifier": "(webpack)\\test\\browsertest\\lib\\index.web.js", // Path to the module
  "moduleName": "./lib/index.web.js", // Relative path to the module
  "name": "main", // The name of the chunk
  "reasons": [
    // A list of the same `reasons` found in [module objects](#module-objects)
  ]
}

模块对象

如果没有对编译应用程序的实际模块的描述,这些统计数据有什么用?依赖图中的每个模块都由以下结构表示:

{
  "assets": [
    // A list of [asset objects](#asset-objects)
  ],
  "built": true, // Indicates that the module went through [Loaders](/concepts/loaders), Parsing, and Code Generation
  "cacheable": true, // Whether or not this module is cacheable
  "chunks": [
    // IDs of chunks that contain this module
  ],
  "errors": 0, // Number of errors when resolving or processing the module
  "failed": false, // Whether or not compilation failed on this module
  "id": 0, // The ID of the module (analogous to [`module.id`](/api/module-variables/#moduleid-commonjs))
  "identifier": "(webpack)\\test\\browsertest\\lib\\index.web.js", // A unique ID used internally
  "name": "./lib/index.web.js", // Path to the actual file
  "optional": false, // All requests to this module are with `try... catch` blocks (irrelevant with ESM)
  "prefetched": false, // Indicates whether or not the module was [prefetched](/plugins/prefetch-plugin)
  "profile": {
    // Module specific compilation stats corresponding to the [`--profile` flag](/api/cli/#profiling) (in milliseconds)
    "building": 73, // Loading and parsing
    "dependencies": 242, // Building dependencies
    "factory": 11 // Resolving dependencies
  },
  "reasons": [
    // See the description below...
  ],
  "size": 3593, // Estimated size of the module in bytes
  "source": "// Should not break it...\r\nif(typeof...", // The stringified raw source
  "warnings": 0 // Number of warnings when resolving or processing the module
}

每个模块还包含一个 reasons 对象列表,描述该模块为何包含在依赖图中。每个“reason”类似于上面块对象部分中看到的 origins

{
  "loc": "33:24-93", // Lines of code that caused the module to be included
  "module": "./lib/index.web.js", // Relative path to the module based on [context](/configuration/entry-context/#context)
  "moduleId": 0, // The ID of the module
  "moduleIdentifier": "(webpack)\\test\\browsertest\\lib\\index.web.js", // Path to the module
  "moduleName": "./lib/index.web.js", // A more readable name for the module (used for "pretty-printing")
  "type": "require.context", // The [type of request](/api/module-methods) used
  "userRequest": "../../cases" // Raw string used for the `import` or `require` request
}

入口对象

"main": {
  "name": "main",
  "chunks": [
    179
  ],
  "assets": [
    {
      "name": "main.js",
      "size": 22
    }
  ],
  "filteredAssets": 0,
  "assetsSize": 22,
  "auxiliaryAssets": [],
  "filteredAuxiliaryAssets": 0,
  "auxiliaryAssetsSize": 0,
  "children": {},
  "childAssets": {},
  "isOverSizeLimit": false
}

错误和警告

errorswarnings 属性都包含一个对象列表。每个对象包含一条消息、一个堆栈跟踪以及各种其他属性:

{
  "moduleIdentifier": "C:\\Repos\\webpack\\test\\cases\\context\\issue-5750\\index.js",
  "moduleName": "(webpack)/test/cases/context/issue-5750/index.js",
  "loc": "3:8-47",
  "message": "Critical dependency: Contexts can't use RegExps with the 'g' or 'y' flags.",
  "moduleId": 29595,
  "moduleTrace": [
    {
      "originIdentifier": "C:\\Repos\\webpack\\test\\cases|sync|/^\\.\\/[^/]+\\/[^/]+\\/index\\.js$/",
      "originName": "(webpack)/test/cases sync ^\\.\\/[^/]+\\/[^/]+\\/index\\.js$",
      "moduleIdentifier": "C:\\Repos\\webpack\\test\\cases\\context\\issue-5750\\index.js",
      "moduleName": "(webpack)/test/cases/context/issue-5750/index.js",
      "dependencies": [
        {
          "loc": "./context/issue-5750/index.js"
        }
      ],
      "originId": 32582,
      "moduleId": 29595
    },
    {
      "originIdentifier": "C:\\Repos\\webpack\\testCases.js",
      "originName": "(webpack)/testCases.js",
      "moduleIdentifier": "C:\\Repos\\webpack\\test\\cases|sync|/^\\.\\/[^/]+\\/[^/]+\\/index\\.js$/",
      "moduleName": "(webpack)/test/cases sync ^\\.\\/[^/]+\\/[^/]+\\/index\\.js$",
      "dependencies": [
        {
          "loc": "1:0-70"
        }
      ],
      "originId": 8198,
      "moduleId": 32582
    }
  ],
  "details": "at RequireContextDependency.getWarnings (C:\\Repos\\webpack\\lib\\dependencies\\ContextDependency.js:79:5)\n    at Compilation.reportDependencyErrorsAndWarnings (C:\\Repos\\webpack\\lib\\Compilation.js:1727:24)\n    at C:\\Repos\\webpack\\lib\\Compilation.js:1467:10\n    at _next2 (<anonymous>:16:1)\n    at eval (<anonymous>:42:1)\n    at C:\\Repos\\webpack\\node_modules\\neo-async\\async.js:2830:7\n    at Object.each (C:\\Repos\\webpack\\node_modules\\neo-async\\async.js:2850:39)\n    at C:\\Repos\\webpack\\lib\\FlagDependencyExportsPlugin.js:219:18\n    at C:\\Repos\\webpack\\node_modules\\neo-async\\async.js:2830:7\n    at Object.each (C:\\Repos\\webpack\\node_modules\\neo-async\\async.js:2850:39)\n    at C:\\Repos\\webpack\\lib\\FlagDependencyExportsPlugin.js:40:16\n    at Hook.eval [as callAsync] (<anonymous>:38:1)\n    at Hook.CALL_ASYNC_DELEGATE [as _callAsync] (C:\\Repos\\tapable\\lib\\Hook.js:18:14)\n    at Compilation.finish (C:\\Repos\\webpack\\lib\\Compilation.js:1462:28)\n    at C:\\Repos\\webpack\\lib\\Compiler.js:909:18\n    at processTicksAndRejections (internal/process/task_queues.js:75:11)\n",
  "stack": "ModuleDependencyWarning: Critical dependency: Contexts can't use RegExps with the 'g' or 'y' flags.\n    at Compilation.reportDependencyErrorsAndWarnings (C:\\Repos\\webpack\\lib\\Compilation.js:1732:23)\n    at C:\\Repos\\webpack\\lib\\Compilation.js:1467:10\n    at _next2 (<anonymous>:16:1)\n    at eval (<anonymous>:42:1)\n    at C:\\Repos\\webpack\\node_modules\\neo-async\\async.js:2830:7\n    at Object.each (C:\\Repos\\webpack\\node_modules\\neo-async\\async.js:2850:39)\n    at C:\\Repos\\webpack\\lib\\FlagDependencyExportsPlugin.js:219:18\n    at C:\\Repos\\webpack\\node_modules\\neo-async\\async.js:2830:7\n    at Object.each (C:\\Repos\\webpack\\node_modules\\neo-async\\async.js:2850:39)\n    at C:\\Repos\\webpack\\lib\\FlagDependencyExportsPlugin.js:40:16\n    at Hook.eval [as callAsync] (<anonymous>:38:1)\n    at Hook.CALL_ASYNC_DELEGATE [as _callAsync] (C:\\Repos\\tapable\\lib\\Hook.js:18:14)\n    at Compilation.finish (C:\\Repos\\webpack\\lib\\Compilation.js:1462:28)\n    at C:\\Repos\\webpack\\lib\\Compiler.js:909:18\n    at processTicksAndRejections (internal/process/task_queues.js:75:11)\n"
}

webpack-dev-server API

webpack-dev-server 提供了一个 Node.js API,可以直接在 Node.js 运行时中使用。

安装

要开始使用 webpack-dev-server Node.js API,如果你尚未安装 webpackwebpack-dev-server,请先安装它们:

npm install --save-dev webpack webpack-dev-server

然后在你的 Node.js 脚本中引入这些模块:

const Webpack = require('webpack');
const WebpackDevServer = require('webpack-dev-server');

start

它指示 webpack-dev-server 实例启动服务器。

server.js

const Webpack = require('webpack');
const WebpackDevServer = require('webpack-dev-server');
const webpackConfig = require('./webpack.config.js');

const compiler = Webpack(webpackConfig);
const devServerOptions = { ...webpackConfig.devServer, open: true };
const server = new WebpackDevServer(devServerOptions, compiler);

const runServer = async () => {
  console.log('Starting server...');
  await server.start();
};

runServer();

然后使用以下命令运行服务器:

node server.js

startCallback(callback)

它指示 webpack-dev-server 实例启动服务器,然后运行回调函数。

server.js

const Webpack = require('webpack');
const WebpackDevServer = require('webpack-dev-server');
const webpackConfig = require('./webpack.config.js');

const compiler = Webpack(webpackConfig);
const devServerOptions = { ...webpackConfig.devServer, open: true };
const server = new WebpackDevServer(devServerOptions, compiler);

server.startCallback(() => {
  console.log('Successfully started server on https://:8080');
});

然后使用以下命令运行服务器:

node server.js

stop

它指示 webpack-dev-server 实例停止服务器。

server.js

const Webpack = require('webpack');
const WebpackDevServer = require('webpack-dev-server');
const webpackConfig = require('./webpack.config.js');

const compiler = Webpack(webpackConfig);
const devServerOptions = { ...webpackConfig.devServer, open: true };
const server = new WebpackDevServer(devServerOptions, compiler);

const runServer = async () => {
  console.log('Starting server...');
  await server.start();
};

const stopServer = async () => {
  console.log('Stopping server...');
  await server.stop();
};

runServer();

setTimeout(stopServer, 5000);

然后使用以下命令运行服务器:

node server.js

stopCallback(callback)

它指示 webpack-dev-server 实例停止服务器,然后运行回调函数。

server.js

const Webpack = require('webpack');
const WebpackDevServer = require('webpack-dev-server');
const webpackConfig = require('./webpack.config.js');

const compiler = Webpack(webpackConfig);
const devServerOptions = { ...webpackConfig.devServer, open: true };
const server = new WebpackDevServer(devServerOptions, compiler);

server.startCallback(() => {
  console.log('Successfully started server on https://:8080');
});

const stopServer = () =>
  server.stopCallback(() => {
    console.log('Server stopped.');
  });

setTimeout(stopServer, 5000);

然后使用以下命令运行服务器:

node server.js

internalIP(family: "v4" | "v6")

异步返回内部 IPv4/IPv6 地址。

server.js

const WebpackDevServer = require('webpack-dev-server');

const logInternalIPs = async () => {
  const localIPv4 = await WebpackDevServer.internalIP('v4');
  const localIPv6 = await WebpackDevServer.internalIP('v6');

  console.log('Local IPv4 address:', localIPv4);
  console.log('Local IPv6 address:', localIPv6);
};

logInternalIPs();

internalIPSync(family: "v4" | "v6")

同步返回内部 IPv4/IPv6 地址。

server.js

const WebpackDevServer = require('webpack-dev-server');

const localIPv4 = WebpackDevServer.internalIPSync('v4');
const localIPv6 = WebpackDevServer.internalIPSync('v6');

console.log('Local IPv4 address:', localIPv4);
console.log('Local IPv6 address:', localIPv6);

热模块替换

如果已通过 HotModuleReplacementPlugin 启用了模块热替换 (Hot Module Replacement),则其接口将通过 module.hot 属性以及 import.meta.webpackHot 属性暴露。请注意,只有 import.meta.webpackHot 可以在严格 ESM 中使用。

通常,用户会检查接口是否可访问,然后开始使用它。例如,你可以这样 accept 一个更新的模块:

if (module.hot) {
  module.hot.accept('./library.js', function () {
    // Do something with the updated library module...
  });
}

// or
if (import.meta.webpackHot) {
  import.meta.webpackHot.accept('./library.js', function () {
    // Do something with the updated library module…
  });
}

支持以下方法:

模块 API

accept

接受给定 dependencies 的更新,并触发 callback 以响应这些更新,此外,你可以附加一个可选的错误处理程序:

module.hot.accept(
  dependencies, // Either a string or an array of strings
  callback, // Function to fire when the dependencies are updated
  errorHandler // (err, {moduleId, dependencyId}) => {}
);

// or
import.meta.webpackHot.accept(
  dependencies, // Either a string or an array of strings
  callback, // Function to fire when the dependencies are updated
  errorHandler // (err, {moduleId, dependencyId}) => {}
);

使用 ESM import 时,dependencies 中的所有导入符号都会自动更新。注意:依赖字符串必须与 import 中的 from 字符串完全匹配。在某些情况下,甚至可以省略 callback。在这里,在 callback 中使用 require() 没有意义。

使用 CommonJS 时,你需要通过在 callback 中使用 require() 手动更新依赖。在这里,省略 callback 没有意义。

accept 的错误处理程序

(err, {moduleId, dependencyId}) => {}

  • err:在使用 ESM 依赖时,由第二个参数中的回调或在依赖执行期间抛出的错误。
  • moduleId:当前模块 ID。
  • dependencyId:(第一个) 更改的依赖模块 ID。

accept (自身)

接受自身的更新。

module.hot.accept(
  errorHandler // Function to handle errors when evaluating the new version
);

// or
import.meta.webpackHot.accept(
  errorHandler // Function to handle errors when evaluating the new version
);

当此模块或依赖项更新时,此模块可以被处置并重新评估,而无需通知父级。如果此模块没有导出(或导出以其他方式更新),则这样做是有意义的。

当此模块(或依赖项)的评估抛出异常时,将触发 errorHandler

自身 accept 的错误处理程序

(err, {moduleId, module}) => {}

  • err:评估新版本时出现的错误。
  • moduleId:当前模块 ID。
  • module:当前模块实例。
    • module.hot:允许使用出错模块实例的 HMR API。常见的情况是再次自身接受它。添加一个处置处理程序来传递数据也是有意义的。请注意,出错的模块可能已经部分执行,因此请确保不要进入不一致状态。你可以使用 module.hot.data 存储部分状态。
    • module.exports:可以被覆盖,但在生产模式下属性名称可能会被混淆,因此请小心。

decline

拒绝给定 dependencies 的更新,强制更新以 'decline' 代码失败。

module.hot.decline(
  dependencies // Either a string or an array of strings
);

// or
import.meta.webpackHot.decline(
  dependencies // Either a string or an array of strings
);

将依赖项标记为不可更新。当无法处理此依赖项的导出更改或尚未实现处理时,这样做是有意义的。根据你的 HMR 管理代码,对这些依赖项(或其未接受的依赖项)的更新通常会导致页面完全重新加载。

decline (自身)

拒绝自身的更新。

module.hot.decline();

// or
import.meta.webpackHot.decline();

将此模块标记为不可更新。当此模块具有不可逆的副作用,或者尚未为此模块实现 HMR 处理时,这样做是有意义的。根据你的 HMR 管理代码,对该模块(或未接受的依赖项)的更新通常会导致页面完全重新加载。

dispose (或 addDisposeHandler)

添加一个处理程序,当当前模块代码被替换时执行。这应该用于移除你已声明或创建的任何持久资源。如果你想将状态转移到更新后的模块,请将其添加到给定的 data 参数中。此对象将在更新后通过 module.hot.data 可用。

module.hot.dispose((data) => {
  // Clean up and pass data to the updated module...
});

// or
import.meta.webpackHot.dispose((data) => {
  // Clean up and pass data to the updated module...
});

invalidate

调用此方法将使当前模块失效,这会在应用 HMR 更新时处置并重新创建它。这会像此模块的正常更新一样冒泡。此模块无法自身接受 invalidate

idle 状态下调用时,将创建包含此模块的新 HMR 更新。HMR 将进入 ready 状态。

readyprepare 状态下调用时,此模块将添加到当前 HMR 更新中。

check 状态下调用时,当有更新可用时,此模块将添加到更新中。如果没有可用更新,它将创建一个新更新。HMR 将进入 ready 状态。

disposeapply 状态下调用时,HMR 将在退出这些状态后将其拾取。

用例

条件接受

模块可以接受依赖,但在无法处理依赖的更改时可以调用 invalidate

import { x, y } from './dep';
import { processX, processY } from 'anotherDep';

const oldY = y;

processX(x);
export default processY(y);

module.hot.accept('./dep', () => {
  if (y !== oldY) {
    // This can't be handled, bubble to parent
    module.hot.invalidate();
    return;
  }
  // This can be handled
  processX(x);
});

条件自身接受

模块可以自身接受,但在无法处理更改时可以使自身失效。

const VALUE = 'constant';

export default VALUE;

if (
  module.hot.data &&
  module.hot.data.value &&
  module.hot.data.value !== VALUE
) {
  module.hot.invalidate();
} else {
  module.hot.dispose((data) => {
    data.value = VALUE;
  });
  module.hot.accept();
}

触发自定义 HMR 更新

const moduleId = chooseAModule();
const code = __webpack_modules__[moduleId].toString();
__webpack_modules__[moduleId] = eval(`(${makeChanges(code)})`);
if (require.cache[moduleId]) {
  require.cache[moduleId].hot.invalidate();
  module.hot.apply();
}

removeDisposeHandler

移除通过 disposeaddDisposeHandler 添加的处理程序。

module.hot.removeDisposeHandler(callback);

// or
import.meta.webpackHot.removeDisposeHandler(callback);

管理 API

status

检索模块热替换进程的当前状态。

module.hot.status(); // Will return one of the following strings...

// or
import.meta.webpackHot.status();
状态描述
空闲进程正在等待对 check 的调用
检查进程正在检查更新
准备进程正在准备更新(例如下载更新的模块)
就绪更新已准备就绪并可用
处置进程正在调用将被替换模块上的 dispose 处理程序
应用进程正在调用 accept 处理程序并重新执行自身接受的模块
中止更新已中止,但系统仍处于其先前的状态
失败更新已抛出异常,系统状态已受损

check

测试所有加载的模块是否有更新,如果存在更新,则 apply 它们。

module.hot
  .check(autoApply)
  .then((outdatedModules) => {
    // outdated modules...
  })
  .catch((error) => {
    // catch errors
  });

// or
import.meta.webpackHot
  .check(autoApply)
  .then((outdatedModules) => {
    // outdated modules...
  })
  .catch((error) => {
    // catch errors
  });

autoApply 参数可以是布尔值或要传递给 apply 方法的 options 对象。

apply

继续更新过程(只要 module.hot.status() === 'ready')。

module.hot
  .apply(options)
  .then((outdatedModules) => {
    // outdated modules...
  })
  .catch((error) => {
    // catch errors
  });

// or
import.meta.webpackHot
  .apply(options)
  .then((outdatedModules) => {
    // outdated modules...
  })
  .catch((error) => {
    // catch errors
  });

可选的 options 对象可以包含以下属性:

  • ignoreUnaccepted (布尔值):忽略对未接受模块的更改。
  • ignoreDeclined (布尔值):忽略对拒绝模块的更改。
  • ignoreErrored (布尔值):忽略在 accept 处理程序、错误处理程序以及重新评估模块时抛出的错误。
  • onDeclined (function(info)):拒绝模块的通知器
  • onUnaccepted (function(info)):未接受模块的通知器
  • onAccepted (function(info)):已接受模块的通知器
  • onDisposed (function(info)):已处置模块的通知器
  • onErrored (function(info)):错误的通知器

info 参数将是一个包含以下部分值的对象:

{
  type: 'self-declined' | 'declined' |
        'unaccepted' | 'accepted' |
        'disposed' | 'accept-errored' |
        'self-accept-errored' | 'self-accept-error-handler-errored',
  moduleId: 4, // The module in question.
  dependencyId: 3, // For errors: the module id owning the accept handler.
  chain: [1, 2, 3, 4], // For declined/accepted/unaccepted: the chain from where the update was propagated.
  parentId: 5, // For declined: the module id of the declining parent
  outdatedModules: [1, 2, 3, 4], // For accepted: the modules that are outdated and will be disposed
  outdatedDependencies: { // For accepted: The location of accept handlers that will handle the update
    5: [4]
  },
  error: new Error(...), // For errors: the thrown error
  originalError: new Error(...) // For self-accept-error-handler-errored:
                                // the error thrown by the module before the error handler tried to handle it.
}

addStatusHandler

注册一个函数来监听 status 的变化。

module.hot.addStatusHandler((status) => {
  // React to the current status...
});

// or
import.meta.webpackHot.addStatusHandler((status) => {
  // React to the current status...
});

请记住,当状态处理程序返回一个 Promise 时,HMR 系统将等待 Promise 解析后才会继续。

removeStatusHandler

移除一个已注册的状态处理程序。

module.hot.removeStatusHandler(callback);

// or
import.meta.webpackHot.removeStatusHandler(callback);

加载器接口

加载器是一个导出函数的 JavaScript 模块。加载器运行器调用此函数,并将上一个加载器的结果或资源文件传递给它。函数的 this 上下文由 webpack 和 加载器运行器填充,其中包含一些有用的方法,允许加载器(除其他功能外)将其调用方式更改为异步,或获取查询参数。

第一个加载器被传递一个参数:资源文件的内容。编译器期望从最后一个加载器获得结果。结果应为 StringBuffer(转换为字符串),表示模块的 JavaScript 源代码。还可以传递一个可选的 SourceMap 结果(作为 JSON 对象)。

同步模式下可以返回单个结果。对于多个结果,必须调用 this.callback() 并且加载器必须返回 undefined

异步模式下,你可以从 async function 返回单个结果。或者,你可以调用 this.async() 来指示 加载器运行器应等待异步结果。它返回 this.callback()。在这种情况下,加载器必须返回 undefined 并调用该回调。这是处理多个结果的唯一选项。

/**
 *
 * @param {string|Buffer} content Content of the resource file
 * @param {object} [map] SourceMap data consumable by https://github.com/mozilla/source-map
 * @param {any} [meta] Meta data, could be anything
 */
function webpackLoader(content, map, meta) {
  // code of your webpack loader
}

示例

以下部分提供不同类型加载器的一些基本示例。请注意,mapmeta 参数是可选的,详见下面的this.callback

同步加载器

可以使用 returnthis.callback 同步返回转换后的 content

sync-loader.js

module.exports = function (content, map, meta) {
  return someSyncOperation(content);
};

this.callback 方法更灵活,因为你可以传递多个参数,而不仅仅是使用 content

sync-loader-with-multiple-results.js

module.exports = function (content, map, meta) {
  this.callback(null, someSyncOperation(content), map, meta);
  return; // always return undefined when calling callback()
};

异步加载器

对于异步加载器,你可以从 async function 返回转换后的 content

async-loader.js

module.exports = async function (content, map, meta) {
  var result = await someAsyncOperation(content);
  return result;
};

或者你可以使用 this.async 来获取 callback 函数。

async-loader-with-callback.js

module.exports = function (content, map, meta) {
  var callback = this.async();
  someAsyncOperation(content, function (err, result) {
    if (err) return callback(err);
    callback(null, result, map, meta);
  });
};

async-loader-with-multiple-results.js

module.exports = function (content, map, meta) {
  var callback = this.async();
  someAsyncOperation(content, function (err, result, sourceMaps, meta) {
    if (err) return callback(err);
    callback(null, result, sourceMaps, meta);
  });
};

“原始”加载器

默认情况下,资源文件被转换为 UTF-8 字符串并传递给加载器。通过将 raw 标志设置为 true,加载器将接收原始的 Buffer。每个加载器都可以将其结果作为 StringBuffer 交付。编译器会在加载器之间进行转换。

raw-loader.js

module.exports = function (content) {
  assert(content instanceof Buffer);
  return someSyncOperation(content);
  // return value can be a `Buffer` too
  // This is also allowed if loader is not "raw"
};
module.exports.raw = true;

Pitching 加载器

加载器总是从右到左调用。在某些情况下,加载器只关心请求背后的元数据,并且可以忽略上一个加载器的结果。加载器上的 pitch 方法在实际执行加载器之前(从右到左)从左到右调用。

对于以下use配置:

module.exports = {
  //...
  module: {
    rules: [
      {
        //...
        use: ['a-loader', 'b-loader', 'c-loader'],
      },
    ],
  },
};

将发生以下步骤:

|- a-loader `pitch`
  |- b-loader `pitch`
    |- c-loader `pitch`
      |- requested module is picked up as a dependency
    |- c-loader normal execution
  |- b-loader normal execution
|- a-loader normal execution

那么,加载器为什么会利用“pitching”阶段呢?

首先,传递给 pitch 方法的 data 在执行阶段也通过 this.data 暴露,这对于捕获和共享生命周期早期信息很有用。

module.exports = function (content) {
  return someSyncOperation(content, this.data.value);
};

module.exports.pitch = function (remainingRequest, precedingRequest, data) {
  data.value = 42;
};

其次,如果加载器在 pitch 方法中交付结果,进程将转向并跳过剩余的加载器。在我们上面的示例中,如果 b-loaderpitch 方法返回了某些内容:

module.exports = function (content) {
  return someSyncOperation(content);
};

module.exports.pitch = function (remainingRequest, precedingRequest, data) {
  if (someCondition()) {
    return (
      'module.exports = require(' +
      JSON.stringify('-!' + remainingRequest) +
      ');'
    );
  }
};

上面的步骤将缩短为:

|- a-loader `pitch`
  |- b-loader `pitch` returns a module
|- a-loader normal execution

加载器上下文

加载器上下文表示加载器内部可用的、分配给 this 属性的属性。

加载器上下文示例

给定以下示例,使用了此 require 调用:

/abc/file.js

require('./loader1?xyz!loader2!./resource?rrr');

this.addContextDependency

addContextDependency(directory: string)

添加一个目录作为加载器结果的依赖项。

this.addDependency

addDependency(file: string)
dependency(file: string) // shortcut

添加现有文件作为加载器结果的依赖项,以便使其可被监视。例如,sass-loaderless-loader 使用此方法在任何导入的 css 文件更改时重新编译。

this.addMissingDependency

addMissingDependency(file: string)

添加一个不存在的文件作为加载器结果的依赖项,以便使其可被监视。类似于 addDependency,但在正确附加监视器之前处理编译期间的文件创建。

this.async

告诉 加载器运行器 加载器打算异步回调。返回 this.callback

this.cacheable

一个设置可缓存标志的函数

cacheable(flag = true: boolean)

默认情况下,加载器结果被标记为可缓存。调用此方法并传递 false 可使加载器结果不可缓存。

可缓存加载器在输入和依赖项未更改时必须具有确定性结果。这意味着加载器不应具有除了使用 this.addDependency 指定的依赖项以外的依赖项。

this.callback

一个可以同步或异步调用的函数,用于返回多个结果。预期的参数是:

this.callback(
  err: Error | null,
  content: string | Buffer,
  sourceMap?: SourceMap,
  meta?: any
);
  1. 第一个参数必须是 Errornull
  2. 第二个参数是 stringBuffer
  3. 可选:第三个参数必须是可由 此模块 解析的源映射。
  4. 可选:第四个选项,webpack 会忽略它,可以是任何东西(例如一些元数据)。

如果调用此函数,应返回 undefined 以避免模糊的加载器结果。

this.clearDependencies

clearDependencies();

删除加载器结果的所有依赖项,甚至是初始依赖项和其他加载器的依赖项。考虑使用 pitch

this.context

模块的目录。 可用作解析其他内容的上下文。

示例中:/abc,因为 resource.js 位于此目录中

this.data

在 pitch 阶段和普通阶段之间共享的数据对象。

this.emitError

emitError(error: Error)

抛出错误,该错误也可以在输出中显示。

ERROR in ./src/lib.js (./src/loader.js!./src/lib.js)
Module Error (from ./src/loader.js):
Here is an Error!
 @ ./src/index.js 1:0-25

this.emitFile

emitFile(name: string, content: Buffer|string, sourceMap: {...})

抛出文件。这是 webpack 特有的。

this.emitWarning

emitWarning(warning: Error)

抛出警告,该警告将按以下方式显示在输出中

WARNING in ./src/lib.js (./src/loader.js!./src/lib.js)
Module Warning (from ./src/loader.js):
Here is a Warning!
 @ ./src/index.js 1:0-25

this.environment

检查生成的运行时代码中可以使用哪些 ES 特性。

例如,

{
  // The environment supports arrow functions ('() => { ... }').
  "arrowFunction": true,
  // The environment supports BigInt as literal (123n).
  "bigIntLiteral": false,
  // The environment supports const and let for variable declarations.
  "const": true,
  // The environment supports destructuring ('{ a, b } = obj').
  "destructuring": true,
  // The environment supports an async import() function to import EcmaScript modules.
  "dynamicImport": false,
  // The environment supports an async import() when creating a worker, only for web targets at the moment.
  "dynamicImportInWorker": false,
  // The environment supports 'for of' iteration ('for (const x of array) { ... }').
  "forOf": true,
  // The environment supports 'globalThis'.
  "globalThis": true,
  // The environment supports ECMAScript Module syntax to import ECMAScript modules (import ... from '...').
  "module": false,
  // The environment supports optional chaining ('obj?.a' or 'obj?.()').
  "optionalChaining": true,
  // The environment supports template literals.
  "templateLiteral": true
}

this.fs

访问 compilationinputFileSystem 属性。

this.getOptions(schema)

提取给定的加载器选项。可选地,接受 JSON schema 作为参数。

this.getResolve

getResolve(options: ResolveOptions): resolve

resolve(context: string, request: string, callback: function(err, result: string))
resolve(context: string, request: string): Promise<string>

创建类似于 this.resolve 的解析函数。

webpack resolve 选项下的任何选项都是可能的。它们与配置的 resolve 选项合并。请注意,数组中可以使用 "..." 来扩展 resolve 选项中的值,例如 { extensions: [".sass", "..."] }

options.dependencyType 是一个附加选项。它允许我们指定依赖项的类型,该类型用于从 resolve 选项中解析 byDependency

解析操作的所有依赖项都会自动添加为当前模块的依赖项。

this.hot

加载器的 HMR 信息。

module.exports = function (source) {
  console.log(this.hot); // true if HMR is enabled via --hot flag or webpack configuration
  return source;
};

this.hashDigest

字符串

5.95.0+

生成哈希时使用的编码。请参阅 output.hashDigest

this.hashDigestLength

数字

5.95.0+

要使用的哈希摘要的前缀长度。请参阅 output.hashDigestLength

this.hashFunction

string function

5.95.0+

要使用的哈希算法。请参阅 output.hashFunction

this.hashSalt

字符串

5.95.0+

一个可选的 salt,用于通过 Node.JS 的 hash.update 更新哈希。请参阅 output.hashSalt

this.importModule

5.32.0+

this.importModule(request, options, [callback]): Promise

子编译器在构建时编译和执行请求的一种替代轻量级解决方案。

  • request:用于从中加载模块的请求字符串
  • 选项:
    • layer:指定此模块放置/编译的层
    • publicPath:用于构建模块的公共路径
  • callback:一个可选的 Node.js 风格回调函数,返回模块的导出或 ESM 的命名空间对象。如果未提供回调,importModule 将返回一个 Promise。

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /stylesheet\.js$/i,
        use: ['./a-pitching-loader.js'],
        type: 'asset/source', // we set type to 'asset/source' as the loader will return a string
      },
    ],
  },
};

a-pitching-loader.js

exports.pitch = async function (remaining) {
  const result = await this.importModule(
    this.resourcePath + '.webpack[javascript/auto]' + '!=!' + remaining
  );
  return result.default || result;
};

src/stylesheet.js

import { green, red } from './colors.js';
export default `body { background: ${red}; color: ${green}; }`;

src/colors.js

export const red = '#f00';
export const green = '#0f0';

src/index.js

import stylesheet from './stylesheet.js';
// stylesheet will be a string `body { background: #f00; color: #0f0; }` at build time

您可能注意到上述示例中的一些内容

  1. 我们有一个 pitching loader
  2. 我们在该 pitching loader 中使用 !=! 语法为请求设置 matchResource,即,我们将使用 this.resourcePath + '.webpack[javascript/auto]' 来匹配 module.rules 而不是原始资源,
  3. .webpack[javascript/auto].webpack[type] 模式的伪扩展,当未指定其他模块类型时,我们用它来指定默认的模块类型。它通常与 !=! 语法结合使用。

请注意,上述示例是一个简化版本,您可以在 webpack 仓库中查看完整示例。

this.loaderIndex

当前加载器在加载器数组中的索引。

示例中:在 loader1 中:0,在 loader2 中:1

this.loadModule

loadModule(request: string, callback: function(err, source, sourceMap, module))

将给定的请求解析为模块,应用所有配置的加载器,并回调生成的源代码、sourceMap 和模块实例(通常是 NormalModule 的实例)。如果需要知道另一个模块的源代码来生成结果,请使用此函数。

加载器上下文中的 this.loadModule 默认使用 CommonJS 解析规则。在使用不同的语义之前,请使用 this.getResolve 和适当的 dependencyType,例如 'esm''commonjs' 或自定义的。

this.loaders

所有加载器的数组。在 pitch 阶段可写。

loaders = [{request: string, path: string, query: string, module: function}]

示例

[
  {
    request: '/abc/loader1.js?xyz',
    path: '/abc/loader1.js',
    query: '?xyz',
    module: [Function],
  },
  {
    request: '/abc/node_modules/loader2/index.js',
    path: '/abc/node_modules/loader2/index.js',
    query: '',
    module: [Function],
  },
];

this.mode

读取 webpack 正在运行的mode

可能的值:'production''development''none'

this.query

  1. 如果加载器是使用 options 对象配置的,这将指向该对象。
  2. 如果加载器没有 options,但使用查询字符串调用,这将是一个以 ? 开头的字符串。

this.request

已解析的请求字符串。

示例中:'/abc/loader1.js?xyz!/abc/node_modules/loader2/index.js!/abc/resource.js?rrr'

this.resolve

resolve(context: string, request: string, callback: function(err, result: string))

解析像 require 表达式一样的请求。

  • context 必须是目录的绝对路径。此目录用作解析的起始位置。
  • request 是要解析的请求。通常使用相对请求(例如 ./relative)或模块请求(例如 module/path),但绝对路径(例如 /some/path)也可以作为请求。
  • callback 是一个普通的 Node.js 风格回调函数,提供已解析的路径。

解析操作的所有依赖项都会自动添加为当前模块的依赖项。

this.resource

请求的资源部分,包括查询字符串。

示例中:'/abc/resource.js?rrr'

this.resourcePath

资源文件。

示例中:'/abc/resource.js'

this.resourceQuery

资源的查询字符串。

示例中:'?rrr'

this.rootContext

从 webpack 4 开始,之前的 this.options.context 作为 this.rootContext 提供。

this.sourceMap

指示是否应生成源映射。由于生成源映射可能是一项耗时的任务,因此您应该检查是否确实请求了源映射。

this.target

编译目标。从配置选项传递。

示例值:'web''node'

this.utils

5.27.0+

访问以下实用工具。

  • absolutify:在可能的情况下使用绝对路径返回新的请求字符串。
  • contextify:在可能的情况下避免使用绝对路径返回新的请求字符串。
  • createHash:从提供的哈希函数返回新的 Hash 对象。

my-sync-loader.js

module.exports = function (content) {
  this.utils.contextify(
    this.context,
    this.utils.absolutify(this.context, './index.js')
  );
  this.utils.absolutify(this.context, this.resourcePath);
  const mainHash = this.utils.createHash(
    this._compilation.outputOptions.hashFunction
  );
  mainHash.update(content);
  mainHash.digest('hex');
  // …
  return content;
};

this.version

加载器 API 版本。 当前为 2。这对于提供向后兼容性很有用。通过此版本,您可以为重大更改指定自定义逻辑或回退。

this.webpack

当此项由 webpack 编译时,此布尔值设置为 true。

Webpack 特有属性

加载器接口提供所有模块相关信息。但在极少数情况下,您可能需要访问编译器 API 本身。

因此,您应该只将其作为最后手段使用。使用它们会降低加载器的可移植性。

this._compilation

访问 webpack 的当前 Compilation 对象。

this._compiler

访问 webpack 的当前 Compiler 对象。

已废弃的上下文属性

this.debug

一个布尔标志。在调试模式下设置。

this.inputValue

从上一个加载器传递。如果您将输入参数作为模块执行,请考虑读取此变量以获取快捷方式(为了性能)。

this.minimize

指示结果是否应最小化。

this.value

将值传递给下一个加载器。如果您知道您的结果在作为模块执行时会导出什么,请在此处设置此值(作为仅包含一个元素的数组)。

this._module

对正在加载的 Module 对象的 hacky 访问。

错误报告

您可以通过以下方式从加载器内部报告错误:

  • 使用 this.emitError。这将报告错误而不会中断模块的编译。
  • 使用 throw(或其他未捕获的异常)。当加载器运行时抛出错误将导致当前模块编译失败。
  • 使用 callback(在异步模式下)。将错误传递给回调也将导致模块编译失败。

例如

./src/index.js

require('./loader!./lib');

从加载器抛出错误

./src/loader.js

module.exports = function (source) {
  throw new Error('This is a Fatal Error!');
};

或在异步模式下将错误传递给回调

./src/loader.js

module.exports = function (source) {
  const callback = this.async();
  //...
  callback(new Error('This is a Fatal Error!'), source);
};

模块将像这样打包

/***/ "./src/loader.js!./src/lib.js":
/*!************************************!*\
  !*** ./src/loader.js!./src/lib.js ***!
  \************************************/
/*! no static exports found */
/***/ (function(module, exports) {

throw new Error("Module build failed (from ./src/loader.js):\nError: This is a Fatal Error!\n    at Object.module.exports (/workspace/src/loader.js:3:9)");

/***/ })

然后构建输出也将显示错误(类似于 this.emitError

ERROR in ./src/lib.js (./src/loader.js!./src/lib.js)
Module build failed (from ./src/loader.js):
Error: This is a Fatal Error!
    at Object.module.exports (/workspace/src/loader.js:2:9)
 @ ./src/index.js 1:0-25

如下所示,不仅有错误消息,还有关于涉及哪个加载器和模块的详细信息

  • 模块路径:ERROR in ./src/lib.js
  • 请求字符串:(./src/loader.js!./src/lib.js)
  • 加载器路径:(from ./src/loader.js)
  • 调用者路径:@ ./src/index.js 1:0-25

内联 matchResource

webpack v4 中引入了一种新的内联请求语法。在请求前加上 <match-resource>!=! 将为此请求设置 matchResource

当设置了 matchResource 时,它将用于与 module.rules 匹配,而不是原始资源。如果需要将更多加载器应用于资源,或者需要更改模块类型,这会很有用。它还会显示在 stats 中,并用于匹配 Rule.issuersplitChunks 中的 test

示例

file.js

/* STYLE: body { background: red; } */
console.log('yep');

加载器可以将文件转换为以下文件,并使用 matchResource 应用用户指定的 CSS 处理规则

file.js(由加载器转换)

import './file.js.css!=!extract-style-loader/getStyles!./file.js';
console.log('yep');

这将为 extract-style-loader/getStyles!./file.js 添加一个依赖项,并将结果视为 file.js.css。因为 module.rules 有一个匹配 /\.css$/ 的规则,它将应用于此依赖项。

加载器可能看起来像这样

extract-style-loader/index.js

const getStylesLoader = require.resolve('./getStyles');

module.exports = function (source) {
  if (STYLES_REGEXP.test(source)) {
    source = source.replace(STYLES_REGEXP, '');
    return `import ${JSON.stringify(
      this.utils.contextify(
        this.context || this.rootContext,
        `${this.resource}.css!=!${getStylesLoader}!${this.remainingRequest}`
      )
    )};${source}`;
  }
  return source;
};

extract-style-loader/getStyles.js

module.exports = function (source) {
  const match = source.match(STYLES_REGEXP);
  return match[0];
};

日志记录

日志 API 从 webpack 4.37 发布以来可用。当 stats 配置中启用 logging 和/或启用 基础设施日志时,加载器可以记录消息,这些消息将以相应的日志格式(stats、infrastructure)打印出来。

  • 加载器应优先使用 this.getLogger() 进行日志记录,这是 compilation.getLogger() 的快捷方式,包含加载器路径和已处理的文件。此类日志记录存储到 Stats 中并相应地格式化。webpack 用户可以对其进行过滤和导出。
  • 加载器可以使用 this.getLogger('name') 获取具有子名称的独立日志记录器。加载器路径和已处理的文件仍然会添加。
  • 加载器可以使用特定的回退逻辑来检测日志记录支持 this.getLogger ? this.getLogger() : console,以便在使用的 webpack 版本较旧且不支持 getLogger 方法时提供回退。

日志记录器接口

日志输出是向最终用户显示消息的另一种方式。

Webpack 日志记录器可用于加载器插件。作为Stats的一部分发出,并由用户在webpack 配置中配置。

webpack 中自定义日志 API 的优势

  • 配置日志显示级别的常用位置
  • 日志输出可作为 stats.json 的一部分导出
  • Stats 预设会影响日志输出
  • 插件可以影响日志捕获和显示级别
  • 当使用多个插件和加载器时,它们使用通用的日志解决方案
  • 用于 webpack 的 CLI、UI 工具可以选择不同的方式来显示日志
  • webpack 核心可以发出日志输出,例如时间数据

通过引入 webpack 日志 API,我们希望统一 webpack 插件和加载器发出日志的方式,并允许以更好的方式检查构建问题。集成日志解决方案通过改善开发体验来支持插件和加载器开发人员。它为非 CLI 的 webpack 解决方案(如仪表板或其他 UI)铺平了道路。

如何在加载器和插件中获取和使用 webpack 日志记录器的示例

my-webpack-plugin.js

const PLUGIN_NAME = 'my-webpack-plugin';
export class MyWebpackPlugin {
  apply(compiler) {
    // you can access Logger from compiler
    const logger = compiler.getInfrastructureLogger(PLUGIN_NAME);
    logger.log('log from compiler');

    compiler.hooks.compilation.tap(PLUGIN_NAME, (compilation) => {
      // you can also access Logger from compilation
      const logger = compilation.getLogger(PLUGIN_NAME);
      logger.info('log from compilation');
    });
  }
}

my-webpack-loader.js

module.exports = function (source) {
  // you can get Logger with `this.getLogger` in your webpack loaders
  const logger = this.getLogger('my-webpack-loader');
  logger.info('hello Logger');
  return source;
};

从上面的 my-webpack-plugin.js 示例可以看出,有两种类型的日志记录方法,

  1. compilation.getLogger
  2. compiler.getInfrastructureLogger

建议在插件/日志记录与编译相关时使用 compilation.getLogger,它们将存储在 stats 中。对于发生在编译周期之外的日志记录,请改用 compiler.getInfrastructureLogger

日志记录器方法

  • logger.error(...):用于错误消息
  • logger.warn(...):用于警告
  • logger.info(...):用于重要信息消息。这些消息默认显示。仅将其用于用户真正需要看到的消息
  • logger.log(...):用于不重要的信息消息。这些消息仅在用户选择查看时显示
  • logger.debug(...):用于调试信息。这些消息仅在用户选择查看特定模块的调试日志时显示
  • logger.trace():显示堆栈跟踪。显示方式类似于 logger.debug
  • logger.group(...):用于分组消息。折叠显示,类似于 logger.log
  • logger.groupEnd():结束日志组
  • logger.groupCollapsed(...):将消息分组。折叠显示,类似于 logger.log。当日志级别设置为 'verbose''debug' 时,会展开显示。
  • logger.status:写入临时消息,设置新状态,覆盖上一个状态
  • logger.clear():打印水平线。显示方式类似于 logger.log
  • logger.profile(...)logger.profileEnd(...):捕获配置文件。当受支持时,委托给 console.profile

运行时日志记录器 API

运行时日志记录器 API 仅旨在用作开发工具,不应包含在生产模式中。

  • const logging = require('webpack/lib/logging/runtime'):要在运行时使用日志记录器,请直接从 webpack 引入它
  • logging.getLogger('name'):按名称获取单个日志记录器
  • logging.configureDefaultLogger(...):覆盖默认日志记录器。
const logging = require('webpack/lib/logging/runtime');
logging.configureDefaultLogger({
  level: 'log',
  debug: /something/,
});
  • logging.hooks.log:将插件应用于运行时日志记录器

模块方法

本节涵盖了使用 webpack 编译的代码中可用的所有方法。当使用 webpack 打包您的应用程序时,您可以选择多种模块语法风格,包括 ES6CommonJSAMD

虽然 webpack 支持多种模块语法,但我们建议遵循单一语法以保持一致性并避免奇怪的行为/错误。实际上,当 .mjs 文件、.cjs 文件或 .js 文件的最近父级 package.json 文件包含 "type" 字段且值为 "module""commonjs" 时,webpack 会强制执行此建议。请在继续阅读之前注意这些强制规定

  • .mjs.jspackage.json 中带有 "type": "module"
    • 不允许使用 CommonJS,例如,您不能使用 requiremodule.exportsexports
    • 导入时需要文件扩展名,例如,您应该使用 import './src/App.mjs' 而不是 import './src/App'(您可以使用 Rule.resolve.fullySpecified 禁用此强制执行)
  • .cjs.jspackage.json 中带有 "type": "commonjs"
    • importexport 都不可用
  • .wasmpackage.json 中带有 "type": "module"
    • 导入 wasm 文件时需要文件扩展名

ES6(推荐)

webpack 版本 2 原生支持 ES6 模块语法,这意味着您可以直接使用 importexport,而无需像 babel 这样的工具来为您处理。请记住,您可能仍然需要 babel 来处理其他 ES6+ 特性。webpack 支持以下方法

import

静态地 import 另一个模块的 export

import MyModule from './my-module.js';
import { NamedExport } from './other-module.js';

您也可以 import Data URI

import 'data:text/javascript;charset=utf-8;base64,Y29uc29sZS5sb2coJ2lubGluZSAxJyk7';
import {
  number,
  fn,
} from 'data:text/javascript;charset=utf-8;base64,ZXhwb3J0IGNvbnN0IG51bWJlciA9IDQyOwpleHBvcnQgY29uc3QgZm4gPSAoKSA9PiAiSGVsbG8gd29ybGQiOw==';

export

将任何内容作为 default 或命名导出。

// Named exports
export var Count = 5;
export function Multiply(a, b) {
  return a * b;
}

// Default export
export default {
  // Some data...
};

import()

function(string path):Promise

动态加载模块。对 import() 的调用被视为分割点,这意味着请求的模块及其子模块被分割成一个单独的 chunk。

if (module.hot) {
  import('lodash').then((_) => {
    // Do something with lodash (a.k.a '_')...
  });
}

import() 中的动态表达式

不可能使用完全动态的 import 语句,例如 import(foo)。因为 foo 可能是您系统或项目中任何文件的任何路径。

import() 必须至少包含有关模块位置的一些信息。打包可以限制到特定目录或文件集,这样当您使用动态表达式时,import() 调用中可能请求的每个模块都将被包含。例如,import(`./locale/${language}.json`) 将导致 ./locale 目录中的每个 .json 文件都被打包到新的 chunk 中。在运行时,当变量 language 被计算后,任何文件(如 english.jsongerman.json)都将可供使用。

// imagine we had a method to get language from cookies or other storage
const language = detectVisitorLanguage();
import(`./locale/${language}.json`).then((module) => {
  // do something with the translations
});

魔法注释

内联注释以使功能生效。通过向 import 添加注释,我们可以命名 chunk 或选择不同的模式。有关这些魔法注释的完整列表,请参阅下面的代码及其解释。

// Single target
import(
  /* webpackChunkName: "my-chunk-name" */
  /* webpackMode: "lazy" */
  /* webpackExports: ["default", "named"] */
  /* webpackFetchPriority: "high" */
  'module'
);

// Multiple possible targets
import(
  /* webpackInclude: /\.json$/ */
  /* webpackExclude: /\.noimport\.json$/ */
  /* webpackChunkName: "my-chunk-name" */
  /* webpackMode: "lazy" */
  /* webpackPrefetch: true */
  /* webpackPreload: true */
  `./locale/${language}`
);

import(/* webpackIgnore: true */ 'ignored-module.js');
webpackIgnore

JavaScript 用法

当设置为 true 时,禁用动态 import 解析。

使用 import.meta.url 时,它不会保持原样;相反,它会根据 baseURI 进行替换。对于模块,它被替换为 new URL("./", import.meta.url);对于其他情况,它默认为 document.baseURI。这确保了相对 URL 能够正常工作,与基本 URL 上下文对齐。

import(/* webpackIgnore: true */ 'ignored-module.js');

new URL(/* webpackIgnore: true */ 'file1.css', import.meta.url);

CSS 用法

webpackIgnore 注释可以控制 webpack 是否处理特定的 import 或 URL 引用。它在某些情况下开箱即用,但出于性能原因,默认情况下不支持所有情况

我们在以下情况下支持 webpackIgnore

@import /* webpackIgnore: false */ url(./basic.css);

.class {
  color: red;
  background: /* webpackIgnore: true */ url('./url/img.png');
}

.class {
  background-image: image-set(
    /*webpackIgnore:  true*/ url(./url/img1x.png) 1x,
    url(./url/img2x.png) 2x,
    url(./url/img3x.png) 3x
  );
}
webpackChunkName

新 chunk 的名称。从 webpack 2.6.0 开始,给定的字符串中支持占位符 [index][request],分别表示递增的数字或实际解析的文件名。添加此注释将使我们的独立 chunk 命名为 [my-chunk-name].js,而不是 [id].js。

webpackFetchPriority
5.87.0+

为特定的动态导入设置 fetchPriority。也可以通过使用 module.parser.javascript.dynamicImportFetchPriority 选项为所有动态导入设置全局默认值。

import(
  /* webpackFetchPriority: "high" */
  'path/to/module'
);
webpackMode

从 webpack 2.6.0 开始,可以指定解析动态导入的不同模式。支持以下选项

  • 'lazy'(默认):为每个 import() 导入的模块生成一个可懒加载的 chunk。
  • 'lazy-once':生成一个可懒加载的单个 chunk,可以满足对 import() 的所有调用。该 chunk 将在第一次调用 import() 时获取,后续对 import() 的调用将使用相同的网络响应。请注意,这仅在部分动态语句的情况下有意义,例如 import(`./locales/${language}.json`),其中可以请求多个模块路径。
  • 'eager':不生成额外的 chunk。所有模块都包含在当前 chunk 中,并且不发出额外的网络请求。Promise 仍然返回,但已经 resolved。与静态导入不同,模块直到调用 import() 时才执行。
  • 'weak':如果模块函数已通过其他方式加载(例如,另一个 chunk 导入了它或加载了包含该模块的脚本),则尝试加载该模块。Promise 仍然返回,但仅当 chunk 已在客户端上时才成功解析。如果模块不可用,则 Promise 被拒绝。永远不会执行网络请求。这对于通用渲染很有用,因为在初始请求中(嵌入页面内)总是手动提供所需的 chunk,但在应用程序导航触发未初始提供的导入的情况下则不适用。
webpackPrefetch

告诉浏览器未来导航可能需要该资源。有关 webpackPrefetch 如何工作的更多信息,请查阅指南。

webpackPreload

告诉浏览器当前导航可能需要该资源。有关 webpackPreload 如何工作的更多信息,请查阅指南。

webpackInclude

一个正则表达式,将在导入解析期间进行匹配。只有匹配的模块才会被打包

webpackExclude

一个正则表达式,将在导入解析期间进行匹配。任何匹配的模块都不会被打包

webpackExports

告诉 webpack 只打包动态 import() 导入模块的指定导出。这可以减小 chunk 的输出大小。自 webpack 5.0.0-beta.18 起可用。

CommonJS

CommonJS 的目标是为浏览器之外的 JavaScript 指定一个生态系统。webpack 支持以下 CommonJS 方法

require

require(dependency: String);

同步检索另一个模块的导出。编译器将确保依赖项在输出捆绑包中可用。

var $ = require('jquery');
var myModule = require('my-module');

require 也可以启用魔法注释,更多信息请参阅 module.parser.javascript.commonjsMagicComments

require.resolve

require.resolve(dependency: String);

同步检索模块的 ID。编译器将确保依赖项在输出捆绑包中可用。建议将其视为一个不透明的值,只能与 require.cache[id]__webpack_require__(id) 一起使用(最好避免此类用法)。

有关更多信息,请参阅 module.id

require.cache

对同一模块的多次 require 调用只会导致一次模块执行和一次导出。因此,运行时存在一个缓存。从该缓存中删除值会导致新的模块执行和新的导出。

var d1 = require('dependency');
require('dependency') === d1;
delete require.cache[require.resolve('dependency')];
require('dependency') !== d1;
// in file.js
require.cache[module.id] === module;
require('./file.js') === module.exports;
delete require.cache[module.id];
require.cache[module.id] === undefined;
require('./file.js') !== module.exports; // in theory; in praxis this causes a stack overflow
require.cache[module.id] !== module;

require.ensure

require.ensure(
  dependencies: String[],
  callback: function(require),
  errorCallback: function(error),
  chunkName: String
)

将给定的 dependencies 分割到一个单独的 bundle 中,该 bundle 将异步加载。当使用 CommonJS 模块语法时,这是动态加载依赖项的唯一方法。这意味着,此代码可以在执行期间运行,仅在满足特定条件时加载 dependencies

var a = require('normal-dep');

if (module.hot) {
  require.ensure(['b'], function (require) {
    var c = require('c');

    // Do something special...
  });
}

按上述顺序支持以下参数

  • dependencies:一个字符串数组,声明 callback 中代码执行所需的所有模块。
  • callback:一个函数,webpack 将在依赖项加载后执行它。require 函数的一个实现作为参数传递给此函数。函数体可以使用此函数进一步 require() 执行所需的模块。
  • errorCallback:当 webpack 未能加载依赖项时执行的函数。
  • chunkName:为此特定的 require.ensure() 创建的 chunk 的名称。通过将相同的 chunkName 传递给各种 require.ensure() 调用,我们可以将它们的代码组合成一个 chunk,从而使浏览器只加载一个 bundle。

AMD

异步模块定义 (AMD) 是一个 JavaScript 规范,它定义了用于编写和加载模块的接口。webpack 支持以下 AMD 方法

define (带 factory)

define([name: String], [dependencies: String[]], factoryMethod: function(...))

如果提供了 dependencies,则 factoryMethod 将与每个依赖项的导出一起调用(按相同顺序)。如果未提供 dependencies,则 factoryMethod 将与 requireexportsmodule 一起调用(为了兼容性!)。如果此函数返回一个值,则该值由模块导出。编译器确保每个依赖项都可用。

define(['jquery', 'my-module'], function ($, myModule) {
  // Do something with $ and myModule...

  // Export a function
  return function doSomething() {
    // ...
  };
});

define (带 value)

define(value: !Function)

这将导出提供的 value。这里的 value 可以是除函数之外的任何内容。

define({
  answer: 42,
});

require (amd-version)

require(dependencies: String[], [callback: function(...)])

require.ensure 类似,这将把给定的 dependencies 分割成一个单独的 bundle,该 bundle 将异步加载。callback 将使用 dependencies 数组中每个依赖项的导出进行调用。

require(['b'], function (b) {
  var c = require('c');
});

标签模块

内部的 LabeledModulesPlugin 使您能够在模块内部使用以下方法进行导出和导入

export label

导出给定的 value。标签可以出现在函数声明或变量声明之前。函数名或变量名是导出值的标识符。

export: var answer = 42;
export: function method(value) {
  // Do something...
};

require label

使依赖项的所有导出在当前作用域中可用。require 标签可以出现在字符串之前。依赖项必须使用 export 标签导出值。CommonJS 或 AMD 模块不能被使用。

some-dependency.js

export: var answer = 42;
export: function method(value) {
  // Do something...
};
require: 'some-dependency';
console.log(answer);
method(...);

Webpack

除了上述模块语法外,webpack 还允许一些自定义的、webpack 特有的方法

require.context

require.context(
  (directory: String),
  (includeSubdirs: Boolean) /* optional, default true */,
  (filter: RegExp) /* optional, default /^\.\/.*$/, any file */,
  (mode: String) /* optional, 'sync' | 'eager' | 'weak' | 'lazy' | 'lazy-once', default 'sync' */
);

使用 directory 的路径、includeSubdirs 选项、用于更精细控制包含模块的 filter 以及定义加载方式的 mode 来指定整个依赖项组。然后可以在以后解析底层模块

var context = require.context('components', true, /\.html$/);
var componentA = context.resolve('componentA');

如果 mode 设置为 'lazy',则底层模块将异步加载

var context = require.context('locales', true, /\.json$/, 'lazy');
context('localeA').then((locale) => {
  // do something with locale
});

可用模式及其行为的完整列表在 import() 文档中描述。

require.include

require.include((dependency: String));

包含一个 dependency 而不执行它。这可用于优化模块在输出 chunk 中的位置。

require.include('a');
require.ensure(['a', 'b'], function (require) {
  /* ... */
});
require.ensure(['a', 'c'], function (require) {
  /* ... */
});

这将产生以下输出

  • 入口 chunk:file.jsa
  • 匿名 chunk:b
  • 匿名 chunk:c

如果没有 require.include('a'),它将在两个匿名 chunk 中重复。

require.resolveWeak

类似于 require.resolve,但它不会将 module 拉入 bundle。这被认为是“弱”依赖项。

if (__webpack_modules__[require.resolveWeak('module')]) {
  // Do something when module is available...
}
if (require.cache[require.resolveWeak('module')]) {
  // Do something when module was loaded before...
}

// You can perform dynamic resolves ("context")
// similarly to other require/import methods.
const page = 'Foo';
__webpack_modules__[require.resolveWeak(`./page/${page}`)];

警告

如果模块源包含无法静态分析的 require,则会发出关键依赖项警告。

示例代码

someFn(require);
require.bind(null);
require(variable);

模块变量

本节涵盖了使用 webpack 编译的代码中可用的所有变量。模块将通过 module 和其他变量访问编译过程中的某些数据。

module.loaded (NodeJS)

如果模块正在执行,则为 false;如果同步执行已完成,则为 true

module.hot (webpack 特有)

指示是否启用热模块替换并提供进程接口。有关详细信息,请参阅HMR API 页面

module.id (CommonJS)

当前模块的 ID。

module.id === require.resolve('./file.js');

module.exports (CommonJS)

定义当消费者对模块进行 require 调用时将返回的值(默认为新对象)。

module.exports = function doSomething() {
  // Do something...
};

exports (CommonJS)

此变量等于 module.exports 的默认值(即一个对象)。如果 module.exports 被覆盖,exports 将不再被导出。

exports.someValue = 42;
exports.anObject = {
  x: 123,
};
exports.aFunction = function doSomething() {
  // Do something
};

global (NodeJS)

请参阅 node.js global

出于兼容性原因,webpack 默认对 global 变量进行 polyfill。

__dirname (NodeJS)

取决于配置选项 node.__dirname

如果在由解析器解析的表达式中使用,则配置选项被视为 true

import.meta

import.meta 向 JavaScript 模块公开上下文特定的元数据,例如模块的 URL。它仅在 ESM 中可用。

请注意,webpack 不支持直接访问 import.meta。相反,您应该访问其属性或使用解构赋值。例如,

// webpack will warn about this
Object.keys(import.meta);

// fine to use
console.log(import.meta.url);
const { url } = import.meta;

import.meta.url

返回模块的绝对 file: URL。

src/index.js

console.log(import.meta.url); // output something like `file:///path/to/your/project/src/index.js`

import.meta.webpack

返回 webpack 版本。

src/index.js

console.log(import.meta.webpack); // output `5` for webpack 5

import.meta.webpackHot

webpack 特有。是 module.hot 的别名,但 import.meta.webpackHot 可用于严格 ESM,而 module.hot 不能。

import.meta.webpackContext

返回与 require.context 相同的值,但仅适用于 javascript/autojavascript/esm

  • 类型

    (
      request: string,
      options?: {
        recursive?: boolean;
        regExp?: RegExp;
        include?: RegExp;
        exclude?: RegExp;
        preload?: boolean | number;
        prefetch?: boolean | number;
        chunkName?: string;
        exports?: string | string[][];
        mode?: 'sync' | 'eager' | 'weak' | 'lazy' | 'lazy-once';
      }
    ) => webpack.Context;
  • 可用版本:5.70.0+

  • 示例

    const contextRequire = import.meta.webpackContext('.', {
      recursive: false,
      regExp: /two/,
      mode: 'weak',
      exclude: /three/,
    });

__filename (NodeJS)

取决于配置选项 node.__filename

如果在由解析器解析的表达式中使用,则配置选项被视为 true

__resourceQuery (webpack 特有)

当前模块的资源查询字符串。如果进行了以下 require 调用,则查询字符串将在 file.js 中可用。

require('file.js?test');

file.js

__resourceQuery === '?test';

__webpack_public_path__ (webpack 特有)

等于配置选项的 output.publicPath

__webpack_require__ (webpack 特有)

原始的 require 函数。此表达式不会被解析器解析为依赖项。

__webpack_chunk_load__ (webpack 特有)

内部 chunk 加载函数。接受一个参数

  • chunkId 要加载的 chunk 的 ID。

从备用公共路径加载 chunk 的示例(当其中一个失败时)

const originalLoad = __webpack_chunk_load__;
const publicPaths = ['a', 'b', 'c'];
__webpack_chunk_load__ = async (id) => {
  let error;
  for (const path of publicPaths) {
    __webpack_public_path__ = path;
    try {
      return await originalLoad(id);
    } catch (e) {
      error = e;
    }
  }
  throw error;
};
import('./module-a').then((moduleA) => {
  // now webpack will use the custom __webpack_chunk_load__ to load chunk
});

__webpack_module__ (webpack 特有)

5.68.0+

它提供对当前 module 的访问。在严格 ESM 中,module 不可用。

__webpack_module__.id (webpack 特有)

5.68.0+

它提供对当前 module 的 ID (module.id) 的访问。在严格 ESM 中,module 不可用。

__webpack_modules__ (webpack 特有)

访问所有模块的内部对象。

__webpack_hash__ (webpack 特有)

它提供对编译哈希的访问。

__webpack_get_script_filename__ (webpack 特有)

function (chunkId)

它根据 chunk 的 ID 提供 chunk 的文件名。

它是可赋值的,允许更改运行时使用的文件名。例如,它可用于在加载 chunk 时确定最终路径。

const oldFn = __webpack_get_script_filename__;

__webpack_get_script_filename__ = (chunkId) => {
  const filename = oldFn(chunkId);
  return filename + '.changed';
};

__non_webpack_require__ (webpack 特有)

生成一个不由 webpack 解析的 require 函数。如果全局 require 函数可用,可用于实现一些很棒的功能。

__webpack_exports_info__ (webpack 特有)

在模块中,__webpack_exports_info__ 可用于允许导出内省

  • __webpack_exports_info__ 始终为 true

  • __webpack_exports_info__.<exportName>.used 在已知导出未使用时为 false,否则为 true

  • __webpack_exports_info__.<exportName>.useInfo

    • 当已知导出未使用时为 false
    • 当已知导出已使用时为 true
    • 当导出使用可能取决于运行时条件时为 null
    • 当没有信息可用时为 undefined
  • __webpack_exports_info__.<exportName>.provideInfo

    • 当已知导出未提供时为 false
    • 当已知导出已提供时为 true
    • 当导出提供可能取决于运行时条件时为 null
    • 当没有信息可用时为 undefined
  • 可以从嵌套导出中访问信息:即 __webpack_exports_info__.<exportName>.<exportName>.<exportName>.used

  • 检查导出是否可以使用 __webpack_exports_info__.<name>.canMangle 进行混淆

__webpack_is_included__ (webpack 特有)

5.16.0+

测试给定模块是否由 webpack 打包。

if (__webpack_is_included__('./module-a.js')) {
  // do something
}

__webpack_base_uri__ (webpack 特有)

在运行时更改基本 URI。

  • 类型:string

  • 可用版本:5.21.0+

  • 示例

    __webpack_base_uri__ = 'https://example.com';

__webpack_runtime_id__

访问当前入口点的运行时 ID。

这是 webpack 特有的功能,自 webpack 5.25.0 起可用。

src/index.js

console.log(__webpack_runtime_id__ === 'main');

DEBUG (webpack 特有)

等于配置选项 debug

编译器 Hook

Compiler 模块是主要引擎,它创建编译实例,并包含通过 CLINode API 传递的所有选项。它扩展了 Tapable 类,以便注册和调用插件。大多数面向用户的插件首先在 Compiler 上注册。

在为 webpack 开发插件时,您可能想知道每个 hook 在哪里被调用。要了解这一点,请在 webpack 源代码中搜索 hooks.<hook name>.call

监听

Compiler 支持监视,它监视文件系统并在文件更改时重新编译。在监视模式下,编译器将发出额外的事件,例如 watchRunwatchCloseinvalid。这通常用于开发中,通常在 webpack-dev-server 等工具的底层,这样开发人员就不必每次都手动重新编译。监视模式也可以通过 CLI 进入。

钩子

compiler 暴露了以下生命周期 hook,可以这样访问

compiler.hooks.someHook.tap('MyPlugin', (params) => {
  /* ... */
});

根据 hook 类型,tapAsynctapPromise 也可能可用。

有关 hook 类型的描述,请参阅Tapable 文档

environment

SyncHook

在准备编译器环境时调用,紧接在配置文件中初始化插件之后。

afterEnvironment

SyncHook

environment hook 之后立即调用,当编译器环境设置完成时。

entryOption

SyncBailHook

在处理 webpack 选项中的 entry 配置之后调用。

compiler.hooks.entryOption.tap('MyPlugin', (context, entry) => {
  /* ... */
});

afterPlugins

SyncHook

在设置完初始内部插件集后调用。

  • 回调参数:compiler

afterResolvers

SyncHook

在解析器设置完成后触发。

  • 回调参数:compiler

initialize

SyncHook

当编译器对象被初始化时调用。

beforeRun

AsyncSeriesHook

在运行编译器之前添加一个 hook。

  • 回调参数:compiler

run

AsyncSeriesHook

在编译器开始读取 records 之前接入编译器。

  • 回调参数:compiler

watchRun

AsyncSeriesHook

在监视模式下,当新的编译被触发但实际编译开始之前执行插件。

  • 回调参数:compiler

normalModuleFactory

SyncHook

在创建 NormalModuleFactory 之后调用。

  • 回调参数:normalModuleFactory

contextModuleFactory

SyncHook

在创建 ContextModuleFactory 之后运行插件。

  • 回调参数:contextModuleFactory

beforeCompile

AsyncSeriesHook

在创建编译参数后执行插件。

  • 回调参数:compilationParams

compilationParams 变量按如下方式初始化

compilationParams = {
  normalModuleFactory,
  contextModuleFactory,
};

此 hook 可用于添加/修改编译参数

compiler.hooks.beforeCompile.tapAsync('MyPlugin', (params, callback) => {
  params['MyPlugin - data'] = 'important stuff my plugin will use later';
  callback();
});

compile

SyncHook

beforeCompile 之后立即调用,在新编译创建之前。此 hook 不会复制到子编译器。

  • 回调参数:compilationParams

thisCompilation

SyncHook

在初始化编译期间执行,在发出 compilation 事件之前。此 hook 不会复制到子编译器。

  • 回调参数:compilationcompilationParams

compilation

SyncHook

在创建编译后运行插件。

  • 回调参数:compilationcompilationParams

make

AsyncParallelHook

在完成编译之前执行。此 hook 不会复制到子编译器。

  • 回调参数:compilation

afterCompile

AsyncSeriesHook

在完成并封装编译后调用。

  • 回调参数:compilation

shouldEmit

SyncBailHook

在发出资产之前调用。应返回一个布尔值,指示是否发出。

  • 回调参数:compilation
compiler.hooks.shouldEmit.tap('MyPlugin', (compilation) => {
  // return true to emit the output, otherwise false
  return true;
});

emit

AsyncSeriesHook

在将资产发出到输出目录之前执行。此 hook 不会复制到子编译器。

  • 回调参数:compilation

afterEmit

AsyncSeriesHook

在将资产发出到输出目录后调用。此 hook 不会复制到子编译器。

  • 回调参数:compilation

assetEmitted

AsyncSeriesHook

当资产已发出时执行。提供有关已发出资产的信息,例如其输出路径和字节内容。

  • 回调参数:fileinfo

例如,您可以通过 info.content 访问资产的内容缓冲区

compiler.hooks.assetEmitted.tap(
  'MyPlugin',
  (file, { content, source, outputPath, compilation, targetPath }) => {
    console.log(content); // <Buffer 66 6f 6f 62 61 72>
  }
);

done

AsyncSeriesHook

当编译完成时执行。此 hook 不会复制到子编译器。

  • 回调参数:stats

additionalPass

AsyncSeriesHook

此 hook 允许您对构建进行一次额外的传递。

failed

SyncHook

如果编译失败则调用。

  • 回调参数:error

invalid

SyncHook

当监视编译无效时执行。此 hook 不会复制到子编译器。

  • 回调参数:fileNamechangeTime

watchClose

SyncHook

当监视编译停止时调用。

shutdown

AsyncSeriesHook

当编译器关闭时调用。

infrastructureLog

SyncBailHook

当通过 infrastructureLogging 选项在配置中启用时,允许使用基础设施日志记录。

  • 回调参数:nametypeargs

log

SyncBailHook

启用时允许记录到 stats 中,请参阅 stats.loggingstats.loggingDebugstats.loggingTrace 选项

  • 回调参数:originlogEntry

编译 Hook

Compilation 模块由 Compiler 用于创建新的编译(或构建)。compilation 实例可以访问所有模块及其依赖项(其中大多数是循环引用)。它是应用程序依赖关系图中所有模块的字面编译。在编译阶段,模块被加载、密封、优化、分块、哈希和恢复。

Compilation 类也扩展了 Tapable 并提供以下生命周期 hook。它们可以与编译器 hook 以相同的方式接入

compilation.hooks.someHook.tap(/* ... */);

与编译器一样,tapAsynctapPromise 也可能可用,具体取决于钩子的类型。

buildModule

SyncHook

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

  • 回调参数:module
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

5.83.0+

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

这是一个如何使用 compilation.hooks.afterChunks 钩子的示例

  • 回调参数:chunks

optimize

SyncHook

在优化阶段开始时触发。

optimizeModules

SyncBailHook

在模块优化阶段开始时调用。插件可以监听此钩子来对模块执行优化。

  • 回调参数:modules

afterOptimizeModules

SyncHook

在模块优化完成后调用。

  • 回调参数:modules

optimizeChunks

SyncBailHook

在 chunk 优化阶段开始时调用。插件可以监听此钩子来对 chunk 执行优化。

  • 回调参数:chunks

afterOptimizeChunks

SyncHook

在 chunk 优化完成后触发。

  • 回调参数:chunks

optimizeTree

AsyncSeriesHook

在优化依赖树之前调用。插件可以监听此钩子来执行依赖树优化。

  • 回调参数:chunks modules

afterOptimizeTree

SyncHook

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

  • 回调参数:chunks modules

optimizeChunkModules

SyncBailHook

在树优化之后,chunk 模块优化开始时调用。插件可以监听此钩子来对 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

为 chunk 创建额外资源。

  • 回调参数:chunks

shouldGenerateChunkAssets

SyncBailHook

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

beforeChunkAssets

SyncHook

在创建 chunk 资源之前执行。

additionalAssets

AsyncSeriesHook

为编译创建额外资源。例如,此钩子可用于下载图片。

compilation.hooks.additionalAssets.tapAsync('MyPlugin', (callback) => {
  download('https://img.shields.io/npm/v/webpack.svg', function (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

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

  • 回调参数:chunks

这是一个为每个 chunk 添加横幅的示例。

compilation.hooks.optimizeChunkAssets.tapAsync(
  'MyPlugin',
  (chunks, callback) => {
    chunks.forEach((chunk) => {
      chunk.files.forEach((file) => {
        compilation.assets[file] = new ConcatSource(
          '/**Sweet Banner**/',
          '\n',
          compilation.assets[file]
        );
      });
    });

    callback();
  }
);

afterOptimizeChunkAssets

SyncHook

chunk 资源已优化。

  • 回调参数:chunks

这是一个来自 @boopathi 的插件示例,它精确输出每个 chunk 的内容。

compilation.hooks.afterOptimizeChunkAssets.tap('MyPlugin', (chunks) => {
  chunks.forEach((chunk) => {
    console.log({
      id: chunk.id,
      name: chunk.name,
      includes: chunk.getModules().map((module) => module.request),
    });
  });
});

optimizeAssets

AsyncSeriesHook

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

  • 回调参数:assets

afterOptimizeAssets

SyncHook

资源已优化。

  • 回调参数:assets

processAssets

AsyncSeriesHook

资源处理。

钩子参数

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

回调参数

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

示例

compilation.hooks.processAssets.tap(
  {
    name: 'MyPlugin',
    stage: Compilation.PROCESS_ASSETS_STAGE_ADDITIONS, // see below for more stages
  },
  (assets) => {
    console.log('List of assets and their sizes:');
    Object.entries(assets).forEach(([pathname, source]) => {
      console.log(`${pathname}: ${source.size()} bytes`);
    });
  }
);

额外资源

除了 namestage,你还可以传入一个 additionalAssets 5.8.0+ 选项,它接受 true 值或一个以 assets 作为第一个参数的回调函数

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

    在此模式下,回调将被调用多次:一次是针对在指定阶段之前添加的资源,额外次数是针对插件稍后添加的资源(在此阶段或后续阶段)。

    compilation.hooks.processAssets.tap(
      {
        name: 'MyPlugin',
        stage: Compilation.PROCESS_ASSETS_STAGE_DEV_TOOLING,
        additionalAssets: true,
      },
      (assets) => {
        // this function will be called multiple times with each bulk of assets
      }
    );
  2. (assets, [callback]) => (void | Promise<void>) — 对插件稍后添加的资源(在此阶段或后续阶段)运行指定的回调。回调必须遵循所使用的 tap 方法的类型(例如,与 tapPromise() 一起使用时,它应该返回一个 promise)。

    compilation.hooks.processAssets.tap(
      {
        name: 'MyPlugin',
        stage: Compilation.PROCESS_ASSETS_STAGE_DEV_TOOLING,
        additionalAssets: (assets) => {
          // this function potentially could be called multiple times for assets added on later stages
        },
      },
      (assets) => {
        // this function will be called once with assets added by plugins on prior stages
      }
    );

资源处理阶段列表

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

  • 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 5.8.0+ — 优化现有资源的数量,例如通过将资源内联到其他资源中。
  • PROCESS_ASSETS_STAGE_SUMMARIZE — 汇总现有资源列表。
  • PROCESS_ASSETS_STAGE_OPTIMIZE_HASH — 优化资源的哈希值,例如通过生成资源内容的真实哈希值。
  • PROCESS_ASSETS_STAGE_OPTIMIZE_TRANSFER — 优化现有资源的传输,例如通过将压缩(gzip)文件准备为单独的资源。
  • PROCESS_ASSETS_STAGE_ANALYSE — 分析现有资源。
  • PROCESS_ASSETS_STAGE_REPORT — 创建用于报告目的的资源。

资源信息

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

示例

compilation.hooks.processAssets.tap(
  {
    /** … */
  },
  (assets) => {
    Object.entries(assets).forEach(([pathname, source]) => {
      const assetInfo = compilation.assetsInfo.get(pathname);
      // @todo: do something with "pathname", "source" and "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 context

这是一个说明性的插件示例

compilation.hooks.statsPreset.for('my-preset').tap('MyPlugin', (options) => {
  if (options.all === undefined) options.all = true;
});

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

statsNormalize

SyncHook

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

  • 回调参数:options context

这是一个说明性的插件示例

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 提供上下文信息,例如路径上的类。

示例

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

ContextModuleFactory 钩子

ContextModuleFactory 模块由 Compiler 使用,从 webpack 特定的 require.context API 生成依赖。它解析请求的目录,为每个文件生成请求并根据传入的正则表达式进行过滤。匹配的依赖随后通过 NormalModuleFactory

ContextModuleFactory 类扩展了 Tapable 并提供了以下生命周期钩子。它们可以像编译器钩子一样被监听。

ContextModuleFactory.hooks.someHook.tap(/* ... */);

与编译器一样,tapAsynctapPromise 也可能可用,具体取决于钩子的类型。

beforeResolve

AsyncSeriesWaterfallHook

在解析请求的目录之前调用。可以通过返回 false 来忽略该请求。

  • 回调参数:data

afterResolve

AsyncSeriesWaterfallHook

在请求的目录解析完成后调用。

  • 回调参数:data

contextModuleFiles

SyncWaterfallHook

在读取目录内容后调用。在递归模式下,也会为每个子目录调用。回调参数是每个目录中所有文件和文件夹名称的数组。

  • 回调参数:fileNames

alternativeRequests

AsyncSeriesWaterfallHook

在创建请求后但在根据正则表达式过滤之前,为每个文件调用。

  • 回调参数:request options

JavascriptParser 钩子

parser 实例位于 compiler 中,用于解析 webpack 处理的每个模块。parser 是另一个扩展 tapable 的 webpack 类,它提供了各种 tapable 钩子,插件作者可以使用这些钩子来自定义解析过程。

parser 位于 NormalModuleFactory 内部,因此需要更多步骤才能访问

compiler.hooks.normalModuleFactory.tap('MyPlugin', (factory) => {
  factory.hooks.parser
    .for('javascript/auto')
    .tap('MyPlugin', (parser, options) => {
      parser.hooks.someHook.tap(/* ... */);
    });
});

与编译器一样,tapAsynctapPromise 也可能可用,具体取决于钩子的类型。

钩子

以下生命周期钩子由 parser 暴露,并可按如下方式访问

evaluateTypeof

SyncBailHook

在评估由自由变量的 typeof 组成的表达式时触发

  • 钩子参数: identifier
  • 回调参数:expression
parser.hooks.evaluateTypeof
  .for('myIdentifier')
  .tap('MyPlugin', (expression) => {
    /* ... */
    return expressionResult;
  });

这将触发 evaluateTypeof 钩子

const a = typeof myIdentifier;

这不会触发

const myIdentifier = 0;
const b = typeof myIdentifier;

evaluate

SyncBailHook

在评估表达式时调用。

  • 钩子参数:expressionType
  • 回调参数:expression

例如

index.js

const a = new String();

MyPlugin.js

parser.hooks.evaluate.for('NewExpression').tap('MyPlugin', (expression) => {
  /* ... */
  return expressionResult;
});

其中表达式类型为

  • 'ArrowFunctionExpression'
  • 'AssignmentExpression'
  • 'AwaitExpression'
  • 'BinaryExpression'
  • 'CallExpression'
  • 'ClassExpression'
  • 'ConditionalExpression'
  • 'FunctionExpression'
  • 'Identifier'
  • 'LogicalExpression'
  • 'MemberExpression'
  • 'NewExpression'
  • 'ObjectExpression'
  • 'SequenceExpression'
  • 'SpreadElement'
  • 'TaggedTemplateExpression'
  • 'TemplateLiteral'
  • 'ThisExpression'
  • 'UnaryExpression'
  • 'UpdateExpression'

evaluateIdentifier

SyncBailHook

在评估作为自由变量的标识符时调用。

  • 钩子参数: identifier
  • 回调参数:expression

evaluateDefinedIdentifier

SyncBailHook

在评估作为已定义变量的标识符时调用。

  • 钩子参数: identifier
  • 回调参数:expression

evaluateCallExpressionMember

SyncBailHook

在评估对已成功评估表达式的成员函数的调用时调用。

  • 钩子参数: identifier
  • 回调参数:expression param

此表达式将触发钩子

index.js

const a = expression.myFunc();

MyPlugin.js

parser.hooks.evaluateCallExpressionMember
  .for('myFunc')
  .tap('MyPlugin', (expression, param) => {
    /* ... */
    return expressionResult;
  });

statement

SyncBailHook

通用钩子,用于代码片段中每个已解析的语句。

  • 回调参数:statement
parser.hooks.statement.tap('MyPlugin', (statement) => {
  /* ... */
});

其中 statement.type 可以是

  • 'BlockStatement'
  • 'VariableDeclaration'
  • 'FunctionDeclaration'
  • 'ReturnStatement'
  • 'ClassDeclaration'
  • 'ExpressionStatement'
  • 'ImportDeclaration'
  • 'ExportAllDeclaration'
  • 'ExportDefaultDeclaration'
  • 'ExportNamedDeclaration'
  • 'IfStatement'
  • 'SwitchStatement'
  • 'ForInStatement'
  • 'ForOfStatement'
  • 'ForStatement'
  • 'WhileStatement'
  • 'DoWhileStatement'
  • 'ThrowStatement'
  • 'TryStatement'
  • 'LabeledStatement'
  • 'WithStatement'

statementIf

SyncBailHook

在解析 if 语句时调用。与 statement 钩子相同,但仅在 statement.type == 'IfStatement' 时触发。

  • 回调参数:statement

label

SyncBailHook

在解析带有标签的语句时调用。这些语句的 statement.type === 'LabeledStatement'

  • 钩子参数:labelName
  • 回调参数:statement

import

SyncBailHook

为代码片段中的每个 import 语句调用。source 参数包含导入文件的名称。

  • 回调参数:statement source

以下 import 语句将触发该钩子一次

index.js

import _ from 'lodash';

MyPlugin.js

parser.hooks.import.tap('MyPlugin', (statement, source) => {
  // source == 'lodash'
});

importSpecifier

SyncBailHook

为每个 import 语句的每个 specifier 调用。

  • 回调参数:statement source exportName identifierName

以下 import 语句将触发该钩子两次

index.js

import _, { has } from 'lodash';

MyPlugin.js

parser.hooks.importSpecifier.tap(
  'MyPlugin',
  (statement, source, exportName, identifierName) => {
    /* First call
    source == 'lodash'
    exportName == 'default'
    identifierName == '_'
  */
    /* Second call
    source == 'lodash'
    exportName == 'has'
    identifierName == 'has'
  */
  }
);

export

SyncBailHook

为代码片段中的每个 export 语句调用。

  • 回调参数:statement

exportImport

SyncBailHook

为每个 export-import 语句调用,例如:export * from 'otherModule';

  • 回调参数:statement source

exportDeclaration

SyncBailHook

为每个导出声明的 export 语句调用。

  • 回调参数:statement declaration

这些 export 将触发此钩子

export const myVar = 'hello'; // also var, let
export function FunctionName() {}
export class ClassName {}

exportExpression

SyncBailHook

为每个导出表达式的 export 语句调用,例如 export default expression;

  • 回调参数:statement declaration

exportSpecifier

SyncBailHook

为每个 export 语句的每个 specifier 调用。

  • 回调参数:statement identifierName exportName index

exportImportSpecifier

SyncBailHook

为每个 export-import 语句的每个 specifier 调用。

  • 回调参数:statement source identifierName exportName index

varDeclaration

SyncBailHook

在解析变量声明时调用。

  • 回调参数:declaration

varDeclarationLet

SyncBailHook

在解析使用 let 定义的变量声明时调用

  • 回调参数:declaration

varDeclarationConst

SyncBailHook

在解析使用 const 定义的变量声明时调用

  • 回调参数:declaration

varDeclarationVar

SyncBailHook

在解析使用 var 定义的变量声明时调用

  • 回调参数:declaration

canRename

SyncBailHook

在重命名标识符之前触发,以确定是否允许重命名。通常与 rename 钩子一起使用。

  • 钩子参数: identifier
  • 回调参数:expression
var a = b;

parser.hooks.canRename.for('b').tap('MyPlugin', (expression) => {
  // returning true allows renaming
  return true;
});

rename

SyncBailHook

在重命名时触发,以获取新的标识符。仅当 canRename 返回 true 时才调用此钩子。

  • 钩子参数: identifier
  • 回调参数:expression
var a = b;

parser.hooks.rename.for('b').tap('MyPlugin', (expression) => {});

assigned

SyncBailHook

在解析 AssignmentExpression 并在解析赋值表达式之前调用。

  • 钩子参数: identifier
  • 回调参数:expression
a += b;

parser.hooks.assigned.for('a').tap('MyPlugin', (expression) => {
  // this is called before parsing b
});

assign

SyncBailHook

在解析 AssignmentExpression 并在解析赋值表达式之前调用。

  • 钩子参数: identifier
  • 回调参数:expression
a += b;

parser.hooks.assigned.for('a').tap('MyPlugin', (expression) => {
  // this is called before parsing a
});

typeof

SyncBailHook

在解析标识符的 typeof 时触发

  • 钩子参数: identifier
  • 回调参数:expression

call

SyncBailHook

在解析函数调用时调用。

  • 钩子参数: identifier
  • 回调参数:expression
eval(/* something */);

parser.hooks.call.for('eval').tap('MyPlugin', (expression) => {});

callMemberChain

SyncBailHook

在解析对对象成员函数的调用时触发。

  • 钩子参数:objectIdentifier
  • 回调参数:expression, properties
myObj.anyFunc();

parser.hooks.callMemberChain
  .for('myObj')
  .tap('MyPlugin', (expression, properties) => {});

new

SyncBailHook

在解析 new 表达式时调用。

  • 钩子参数: identifier
  • 回调参数:expression
new MyClass();

parser.hooks.new.for('MyClass').tap('MyPlugin', (expression) => {});

expression

SyncBailHook

在解析表达式时调用。

  • 钩子参数: identifier
  • 回调参数:expression
const a = this;

parser.hooks.expression.for('this').tap('MyPlugin', (expression) => {});

expressionConditionalOperator

SyncBailHook

在解析 ConditionalExpression 时调用,例如 condition ? a : b

  • 回调参数:expression

program

SyncBailHook

获取代码片段的抽象语法树(AST)

  • 参数:ast comments

NormalModuleFactory 钩子

NormalModuleFactory 模块由 Compiler 使用,用于生成模块。从入口点开始,它解析每个请求,解析内容以查找更多请求,并通过解析所有新文件和解析任何新文件来持续遍历文件。在最后阶段,每个依赖都成为一个 Module 实例。

NormalModuleFactory 类扩展了 Tapable 并提供了以下生命周期钩子。它们可以像编译器钩子一样被监听。

NormalModuleFactory.hooks.someHook.tap(/* ... */);

NormalModuleFactory 创建 ParserGenerator 实例,这些实例可以通过 HookMaps 访问。必须传递标识符才能监听这些钩子。

NormalModuleFactory.hooks.someHook.for('identifier').tap(/* ... */);

与编译器一样,tapAsynctapPromise 也可能可用,具体取决于钩子的类型。

beforeResolve

AsyncSeriesBailHook

在遇到新的依赖请求时调用。可以通过返回 false 来忽略依赖。否则,应返回 undefined 以继续。

  • 回调参数:resolveData

factorize

AsyncSeriesBailHook

在开始解析之前调用。它应该返回 undefined 以继续。

  • 回调参数:resolveData

resolve

AsyncSeriesBailHook

在请求解析之前调用。可以通过返回 false 来忽略依赖。返回一个 Module 实例将终止该过程。否则,应返回 undefined 以继续。

  • 回调参数:resolveData

resolveForScheme

AsyncSeriesBailHook

在解析带有方案(URI)的请求之前调用。

  • 回调参数:resolveData

afterResolve

AsyncSeriesBailHook

在请求解析完成后调用。

  • 回调参数:resolveData

createModule

AsyncSeriesBailHook

在创建 NormalModule 实例之前调用。

  • 回调参数:createData resolveData

createModuleClass

HookMap<SyncBailHook>

5.81.0+

一个钩子,允许您在创建模块时覆盖 NormalModule 类。此钩子在 createModule 钩子之后和 module 钩子之前调用。

  • 钩子参数: identifier

  • 回调参数:createData resolveData

module

SyncWaterfallHook

在创建 NormalModule 实例之后调用。

  • 回调参数:module createData resolveData

createParser

HookMap<SyncBailHook>

在创建 Parser 实例之前调用。parserOptionsmodule.parser 中对应标识符的选项,或一个空对象。

  • 钩子参数: identifier

  • 回调参数:parserOptions

parser

HookMap<SyncHook>

在创建 Parser 实例后触发。

  • 钩子参数: identifier

  • 回调参数:parser parserOptions

可能的默认标识符

  1. javascript/auto
  2. javascript/dynamic
  3. javascript/esm
  4. JSON
  5. webassembly/sync
  6. webassembly/async
  7. asset

createGenerator

HookMap<SyncBailHook>

在创建 Generator 实例之前调用。generatorOptionsmodule.parser 中对应标识符的选项,或一个空对象。

  • 钩子参数: identifier

  • 回调参数:generatorOptions

generator

HookMap<SyncHook>

在创建 Generator 实例之后调用。

  • 钩子参数: identifier

  • 回调参数:generator generatorOptions

可能的默认标识符

  1. JSON
  2. webassembly/sync
  3. webassembly/async
  4. asset
  5. asset/source
  6. asset/resource
  7. asset/inline

Compilation 对象

Compilation 对象有许多可用的方法和钩子。在此页面,我们将列出可用的方法和属性。

compilation 对象方法

getStats

function

返回当前编译的 Stats 对象。

addModule

function (module, callback)

向当前编译添加模块。

参数

  • module - 要添加的模块
  • callback - 模块添加后的回调函数

getModule

function (module)

根据标识符从编译中获取模块。

参数

  • module - 要获取的模块。标识符通过 compilation 使用 module.identifier() 方法从模块中提取。

findModule

function (module)

尝试按标识符搜索模块。

参数

  • module - 要搜索的模块。标识符通过 compilation 使用 module.identifier() 方法从模块中提取。

buildModule

function (module, optional, origin, dependencies)

构建给定模块。

参数

  • module - 要构建的模块。
  • optional - 可选标志。
  • origin - 请求此模块构建的原始模块。
  • dependencies - 要构建模块的可选依赖项。

processModuleDependencies

function (module, callback)

处理给定模块的依赖项。

参数

  • module - 要处理其依赖项的模块。
  • callback - 模块依赖项处理完成后要调用的函数。

addEntry

function (context, entry, name, callback)

向编译添加一个入口。

参数

  • context - 入口上下文路径。
  • entry - 入口依赖项。
  • name - 入口的名称。
  • callback - addEntry 完成后要调用的函数。

rebuildModule

function (module, thisCallback)

触发模块的重建。

参数

  • module - 要重建的模块。
  • thisCallback - 模块重建完成后要调用的函数。

finish

function (callback)

完成编译并调用给定回调。

参数

  • callback - 编译完成后要调用的函数。

seal

function (callback)

密封编译。

参数

  • callback - 编译密封完成后要调用的函数。

unseal

function

解封编译。

参数

  • callback - 编译解封完成后要调用的函数。

reportDependencyErrorsAndWarnings

function (module, blocks)

将给定模块的错误和警告添加到编译的错误和警告中。

参数

  • module - 要报告其错误和警告的模块。
  • blocks - 一组要报告的依赖块。

addChunkInGroup

function (groupOptions, module, loc, request)

将模块添加到现有 chunk 组或创建新组。返回一个 chunkGroup

参数

  • groupOptions - chunk 组的选项。
  • module - 引用 chunk 组的模块。
  • loc - 引用 chunk 组的位置(模块内部)。
  • request - 引用 chunk 组的请求。

addChunk

function (name)

创建新 chunk 并将其添加到 compilation.chunks。返回该 chunk

参数

  • name - chunk 的名称。

assignDepth

function (module)

递归地为给定模块及其依赖块分配 depth

参数

  • module - 要分配深度的模块。

getDependencyReference

function (module, dependency)

从给定模块返回对依赖项的引用。

参数

  • module - 相关模块。
  • dependency - 要获取引用的依赖项。

processDependenciesBlocksForChunkGroups

function (inputChunkGroups)

Module 图创建 Chunk 图。该过程分两个阶段完成。第一阶段:遍历模块图并在 chunkDependencies 中构建基本的 chunk 图。第二阶段:遍历基本 chunk 图的每种可能方式并跟踪可用模块。在遍历过程中,processDependenciesBlocksForChunkGroups 将 chunk 相互连接,并将 BlocksChunks 连接。当 chunk 的所有模块都已可用时,它会停止遍历,并且不会连接不需要的 chunk。

参数

  • inputChunkGroups - 正在处理的 chunk 组。

removeReasonsOfDependencyBlock

function (module, block)

移除模块与依赖块的关系。

参数

  • module - 要移除模块关系。
  • block - 依赖块。

patchChunksAfterReasonRemoval

function (module, chunk)

在移除依赖原因后,修补模块和 chunk 的关联。由 removeReasonsOfDependencyBlock 自动调用。

参数

  • module - 要修补关联的模块。
  • chunk - 要修补关联的 chunk。

removeChunkFromDependencies

function (block, chunk)

在移除依赖原因后,从依赖块模块和 chunk 中移除给定 chunk。由 removeReasonsOfDependencyBlock 自动调用。

参数

  • block - Chunk 的块关联。
  • chunk - 要从依赖项中移除的 chunk。

sortItemsWithChunkIds

function

summarizeDependencies

function

createHash

function

createModuleAssets

function

createChunkAssets

function

getPath

function (filename, data)

返回插值路径。

参数

  • filename - 用于获取带哈希的资源路径。
  • data - 数据对象。

getPathWithInfo

function (filename, data)

返回插值路径和资源信息。

参数

  • filename - 用于获取带哈希的资源路径。
  • data - 数据对象。

createChildCompiler

function (name, outputOptions, plugins)

允许在 webpack 内部运行另一个 webpack 实例。但是,作为子实例,它具有不同的设置和配置。它从父(或顶级)编译器复制所有钩子和插件,并创建一个子 Compiler 实例。返回创建的 Compiler

参数

  • name - 子 Compiler 的名称。
  • outputOptions - 输出选项对象。
  • plugins - 将应用的 webpack 插件。

checkConstraints

function

emitAsset

function (file, source, assetInfo = {})

参数

  • file - 资源的 文件名
  • source - 资源的 源
  • assetInfo - 额外资源信息

updateAsset

function (file, newSourceOrFunction, assetInfoUpdateOrFunction)

参数

  • file - 资源的 文件名
  • newSourceOrFunction - 新资源源或将旧源转换为新源的函数
  • assetInfoUpdateOrFunction - 新资源信息或将旧信息转换为新信息的函数

deleteAsset

function (file)

参数

  • file - 资源的 文件名

getAssets

function

返回当前编译下所有资源的数组。

getAsset

function (name)

参数

  • name - 要返回的资源名称

插件 API

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

我们首先介绍 tapable 工具,它提供了 webpack 插件接口的骨干。

Tapable

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

请参阅文档了解更多信息。理解这三个 tap 方法以及提供它们的钩子至关重要。扩展 Tapable 的对象(例如 compiler)、它们提供的钩子以及每个钩子的类型(例如 SyncHook)都将被注明。

插件类型

根据所使用的钩子和应用的 tap 方法,插件可以以不同的方式运行。其工作原理与 Tapable 提供的钩子密切相关。编译器钩子会注明底层 Tapable 钩子,指示哪些 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.');
  }
);

这个故事的寓意是,有多种方法可以钩入 compiler,每种方法都允许您的插件以其认为合适的方式运行。

自定义钩子

为了向编译提供自定义钩子以供其他插件监听,您需要执行以下操作:

  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
    });

再次强调,请参阅 tapable 文档以了解不同钩子类及其工作原理的更多信息。

报告进度

插件可以通过 ProgressPlugin 报告进度,该插件默认将进度消息打印到标准错误输出。要启用进度报告,在运行 webpack CLI 时传递 --progress 参数。

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

要报告进度,插件必须使用 context: true 选项监听钩子

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 configuration 中启用日志记录和/或启用 infrastructure logging 时,插件可以记录消息,这些消息将以相应的日志格式(统计信息、基础设施)打印出来。

  • 插件应优先使用 compilation.getLogger('PluginName') 进行日志记录。这种日志记录存储在 Stats 中并相应地格式化。用户可以对其进行过滤和导出。
  • 插件可以使用 compiler.getInfrastructureLogger('PluginName') 进行日志记录。使用基础设施日志记录不会存储在 Stats 中,因此不会格式化。它通常直接记录到控制台/仪表板/GUI。用户可以对其进行过滤。
  • 插件可以使用特定的回退逻辑来检测日志支持:compilation.getLogger ? compilation.getLogger('PluginName') : console,以便在使用了不支持 compilation 对象上的 getLogger 方法的旧 webpack 版本时提供回退。

下一步

请参阅编译器钩子部分,获取所有可用编译器钩子的详细列表及其提供的参数。

解析器

解析器是使用 enhanced-resolve 包创建的。Resolver 类扩展了 tapable 类,并使用 tapable 提供了一些钩子。enhanced-resolve 包可以直接用于创建新的解析器,但是任何compiler 实例都有一些可以监听的解析器实例。

在继续阅读之前,请务必查看 enhanced-resolvetapable 文档。

类型

compiler 类中有三种内置解析器类型可用

  • normal: 通过绝对或相对路径解析模块。
  • context: 在给定上下文中解析模块。
  • loader: 解析 webpack loader

根据需要,compiler 使用的任何这些内置解析器都可以通过插件进行自定义

compiler.resolverFactory.hooks.resolver
  .for('[type]')
  .tap('name', (resolver) => {
    // you can tap into resolver.hooks now
    resolver.hooks.result.tap('MyPlugin', (result) => {
      return result;
    });
  });

其中 [type] 是上面提到的三种解析器之一。

请参阅 enhanced-resolve 文档,了解钩子的完整列表及其描述。

配置选项

上述解析器也可以通过配置文件中的 resolveresolveLoader 选项进行自定义。这些选项允许用户通过包括解析插件在内的各种选项来更改解析行为。

解析器插件(例如 DirectoryNamedPlugin)可以直接包含在 resolve.plugins 中,而不是直接在 plugins 配置选项中使用。

1 贡献者

webpack