多种接口可用于自定义编译过程。某些功能在接口之间重叠,例如,配置选项可能通过 CLI 标志可用,而其他功能则仅通过单个接口存在。以下高级信息应能帮助你入门。
命令行界面 (CLI) 用于配置和与你的构建交互。它在早期原型设计和性能分析中特别有用。在大多数情况下,CLI 用于使用配置文件和一些标志(例如 --env
)来启动进程。
在使用 webpack 处理模块时,了解所支持的不同模块语法(特别是方法和变量)非常重要。
虽然大多数用户可以使用 CLI 和配置文件,但通过 Node 接口可以实现对编译更细粒度的控制。这包括传递多个配置、以编程方式运行或监视以及收集统计信息。
加载器是对模块源代码应用的转换。它们被写成函数,接受源代码作为参数,并返回应用了转换的新版本代码。
插件接口允许用户直接参与编译过程。插件可以在生命周期钩子(在编译过程中的不同点运行)上注册处理程序。当每个钩子执行时,插件将完全访问编译的当前状态。
为了正确使用和更方便地分发此配置,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 [命令...] | 输出 webpack 、webpack-cli 和 webpack-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
作为构建项目入口点的文件名或一组命名文件名。你可以传递多个入口(每个入口都在启动时加载)。以下是通过 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
。
示例
如果你的项目结构如下 -
.
├── 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
显示已安装包和子包的版本
要检查你正在使用的 webpack
和 webpack-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
使用配置文件构建源
指定不同于 webpack.config.js
的配置文件,webpack.config.js
是默认配置文件之一。
npx webpack --config example.config.js
如果你的配置文件导出了多个配置,你可以使用 --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
你可以借助 --extends
扩展现有 webpack 配置
npx webpack --extends ./base.webpack.config.js
在扩展配置中了解更多信息。
将 webpack 结果打印为 JSON
npx webpack --json
如果你想将统计数据存储为 JSON 而不是打印出来,你可以使用 -
npx webpack --json stats.json
在所有其他情况下,webpack 会打印一组统计数据,显示打包、块和时间细节。使用此选项,输出可以是 JSON 对象。此响应可被 webpack 的分析工具、chrisbateman 的webpack-visualizer 或 th0r 的webpack-bundle-analyzer 接受。分析工具将接收 JSON 并以图形形式提供构建的所有详细信息。
当 webpack 配置导出一个函数时,可以向其传递一个“环境”。
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
选项来设置 process.env.NODE_ENV
,它对用户代码和 webpack 配置都可用。
npx webpack --node-env production # process.env.NODE_ENV = 'production'
--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_ENV
和 mode
都设置为 'production'
。
如果你的配置导出一个函数,--config-node-env
的值将在函数返回后分配给 mode。这意味着 mode
将在函数参数(env
和 argv
)中不可用。但是,--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
要将参数直接传递给 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 | 配置/选项问题或内部错误 |
环境变量 | 描述 |
---|---|
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
在 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
在使用 TypeScript 中的原生 ESM(即 package.json
中的 type: "module"
)时,你可能会遇到此错误。
webpack-cli
支持 CommonJS
和 ESM
格式的配置,它首先尝试使用 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 配置的文档。
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 编译器。
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
生命周期中的任何钩子事件。WebpackOptionsDefaulter
和 WebpackOptionsApply
工具由 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
对象公开以下方法:
可用于检查编译时是否存在错误。返回 true
或 false
。
可用于检查编译时是否存在警告。返回 true
或 false
。
将编译信息作为 JSON 对象返回。options
可以是字符串(预设)或对象,以实现更精细的控制。
stats.toJson('minimal');
stats.toJson({
assets: false,
hash: true,
});
所有可用选项和预设都在统计文档中描述。
这是此函数输出的一个示例。
返回编译信息的格式化字符串(类似于 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');
}
);
为了良好的错误处理,你需要考虑以下三种类型的错误:
这是实现所有这些的一个示例:
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 等)来改变输入或输出行为。为此,可以更改 inputFileSystem
或 outputFileSystem
。例如,你可以用 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
}
errors
和 warnings
属性都包含一个对象列表。每个对象包含一条消息、一个堆栈跟踪以及各种其他属性:
{
"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
提供了一个 Node.js API,可以直接在 Node.js 运行时中使用。
要开始使用 webpack-dev-server
Node.js API,如果你尚未安装 webpack
和 webpack-dev-server
,请先安装它们:
npm install --save-dev webpack webpack-dev-server
然后在你的 Node.js 脚本中引入这些模块:
const Webpack = require('webpack');
const WebpackDevServer = require('webpack-dev-server');
它指示 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
它指示 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
它指示 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
它指示 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
异步返回内部 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();
同步返回内部 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…
});
}
支持以下方法:
接受给定 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
没有意义。
(err, {moduleId, dependencyId}) => {}
err
:在使用 ESM 依赖时,由第二个参数中的回调或在依赖执行期间抛出的错误。moduleId
:当前模块 ID。dependencyId
:(第一个) 更改的依赖模块 ID。接受自身的更新。
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
。
(err, {moduleId, module}) => {}
err
:评估新版本时出现的错误。moduleId
:当前模块 ID。module
:当前模块实例。
module.hot
:允许使用出错模块实例的 HMR API。常见的情况是再次自身接受它。添加一个处置处理程序来传递数据也是有意义的。请注意,出错的模块可能已经部分执行,因此请确保不要进入不一致状态。你可以使用 module.hot.data
存储部分状态。module.exports
:可以被覆盖,但在生产模式下属性名称可能会被混淆,因此请小心。拒绝给定 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 管理代码,对这些依赖项(或其未接受的依赖项)的更新通常会导致页面完全重新加载。
拒绝自身的更新。
module.hot.decline();
// or
import.meta.webpackHot.decline();
将此模块标记为不可更新。当此模块具有不可逆的副作用,或者尚未为此模块实现 HMR 处理时,这样做是有意义的。根据你的 HMR 管理代码,对该模块(或未接受的依赖项)的更新通常会导致页面完全重新加载。
添加一个处理程序,当当前模块代码被替换时执行。这应该用于移除你已声明或创建的任何持久资源。如果你想将状态转移到更新后的模块,请将其添加到给定的 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...
});
调用此方法将使当前模块失效,这会在应用 HMR 更新时处置并重新创建它。这会像此模块的正常更新一样冒泡。此模块无法自身接受 invalidate
。
在 idle
状态下调用时,将创建包含此模块的新 HMR 更新。HMR 将进入 ready
状态。
在 ready
或 prepare
状态下调用时,此模块将添加到当前 HMR 更新中。
在 check
状态下调用时,当有更新可用时,此模块将添加到更新中。如果没有可用更新,它将创建一个新更新。HMR 将进入 ready
状态。
在 dispose
或 apply
状态下调用时,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();
}
移除通过 dispose
或 addDisposeHandler
添加的处理程序。
module.hot.removeDisposeHandler(callback);
// or
import.meta.webpackHot.removeDisposeHandler(callback);
检索模块热替换进程的当前状态。
module.hot.status(); // Will return one of the following strings...
// or
import.meta.webpackHot.status();
状态 | 描述 |
---|---|
空闲 | 进程正在等待对 check 的调用 |
检查 | 进程正在检查更新 |
准备 | 进程正在准备更新(例如下载更新的模块) |
就绪 | 更新已准备就绪并可用 |
处置 | 进程正在调用将被替换模块上的 dispose 处理程序 |
应用 | 进程正在调用 accept 处理程序并重新执行自身接受的模块 |
中止 | 更新已中止,但系统仍处于其先前的状态 |
失败 | 更新已抛出异常,系统状态已受损 |
测试所有加载的模块是否有更新,如果存在更新,则 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
对象。
继续更新过程(只要 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.
}
注册一个函数来监听 status
的变化。
module.hot.addStatusHandler((status) => {
// React to the current status...
});
// or
import.meta.webpackHot.addStatusHandler((status) => {
// React to the current status...
});
请记住,当状态处理程序返回一个 Promise
时,HMR 系统将等待 Promise
解析后才会继续。
移除一个已注册的状态处理程序。
module.hot.removeStatusHandler(callback);
// or
import.meta.webpackHot.removeStatusHandler(callback);
加载器是一个导出函数的 JavaScript 模块。加载器运行器调用此函数,并将上一个加载器的结果或资源文件传递给它。函数的 this
上下文由 webpack 和 加载器运行器填充,其中包含一些有用的方法,允许加载器(除其他功能外)将其调用方式更改为异步,或获取查询参数。
第一个加载器被传递一个参数:资源文件的内容。编译器期望从最后一个加载器获得结果。结果应为 String
或 Buffer
(转换为字符串),表示模块的 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
}
以下部分提供不同类型加载器的一些基本示例。请注意,map
和 meta
参数是可选的,详见下面的this.callback
。
可以使用 return
或 this.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
。每个加载器都可以将其结果作为 String
或 Buffer
交付。编译器会在加载器之间进行转换。
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;
加载器总是从右到左调用。在某些情况下,加载器只关心请求背后的元数据,并且可以忽略上一个加载器的结果。加载器上的 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-loader
的 pitch
方法返回了某些内容:
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');
addContextDependency(directory: string)
添加一个目录作为加载器结果的依赖项。
addDependency(file: string)
dependency(file: string) // shortcut
添加现有文件作为加载器结果的依赖项,以便使其可被监视。例如,sass-loader
、less-loader
使用此方法在任何导入的 css
文件更改时重新编译。
addMissingDependency(file: string)
添加一个不存在的文件作为加载器结果的依赖项,以便使其可被监视。类似于 addDependency
,但在正确附加监视器之前处理编译期间的文件创建。
告诉 加载器运行器 加载器打算异步回调。返回 this.callback
。
一个设置可缓存标志的函数
cacheable(flag = true: boolean)
默认情况下,加载器结果被标记为可缓存。调用此方法并传递 false
可使加载器结果不可缓存。
可缓存加载器在输入和依赖项未更改时必须具有确定性结果。这意味着加载器不应具有除了使用 this.addDependency
指定的依赖项以外的依赖项。
一个可以同步或异步调用的函数,用于返回多个结果。预期的参数是:
this.callback(
err: Error | null,
content: string | Buffer,
sourceMap?: SourceMap,
meta?: any
);
Error
或 null
string
或 Buffer
。如果调用此函数,应返回 undefined 以避免模糊的加载器结果。
clearDependencies();
删除加载器结果的所有依赖项,甚至是初始依赖项和其他加载器的依赖项。考虑使用 pitch
。
模块的目录。 可用作解析其他内容的上下文。
在示例中:/abc
,因为 resource.js
位于此目录中
在 pitch 阶段和普通阶段之间共享的数据对象。
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
emitFile(name: string, content: Buffer|string, sourceMap: {...})
抛出文件。这是 webpack 特有的。
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
检查生成的运行时代码中可以使用哪些 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
}
访问 compilation
的 inputFileSystem
属性。
提取给定的加载器选项。可选地,接受 JSON schema 作为参数。
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
。
解析操作的所有依赖项都会自动添加为当前模块的依赖项。
加载器的 HMR 信息。
module.exports = function (source) {
console.log(this.hot); // true if HMR is enabled via --hot flag or webpack configuration
return source;
};
字符串
生成哈希时使用的编码。请参阅 output.hashDigest。
数字
要使用的哈希摘要的前缀长度。请参阅 output.hashDigestLength。
string
function
要使用的哈希算法。请参阅 output.hashFunction。
字符串
一个可选的 salt,用于通过 Node.JS 的 hash.update
更新哈希。请参阅 output.hashSalt。
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
您可能注意到上述示例中的一些内容
!=!
语法为请求设置 matchResource,即,我们将使用 this.resourcePath + '.webpack[javascript/auto]'
来匹配 module.rules
而不是原始资源,.webpack[javascript/auto]
是 .webpack[type]
模式的伪扩展,当未指定其他模块类型时,我们用它来指定默认的模块类型。它通常与 !=!
语法结合使用。请注意,上述示例是一个简化版本,您可以在 webpack 仓库中查看完整示例。
当前加载器在加载器数组中的索引。
在示例中:在 loader1 中:0
,在 loader2 中:1
loadModule(request: string, callback: function(err, source, sourceMap, module))
将给定的请求解析为模块,应用所有配置的加载器,并回调生成的源代码、sourceMap 和模块实例(通常是 NormalModule
的实例)。如果需要知道另一个模块的源代码来生成结果,请使用此函数。
加载器上下文中的 this.loadModule
默认使用 CommonJS 解析规则。在使用不同的语义之前,请使用 this.getResolve
和适当的 dependencyType
,例如 'esm'
、'commonjs'
或自定义的。
所有加载器的数组。在 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],
},
];
读取 webpack 正在运行的mode
。
可能的值:'production'
、'development'
、'none'
options
对象配置的,这将指向该对象。options
,但使用查询字符串调用,这将是一个以 ?
开头的字符串。已解析的请求字符串。
在示例中:'/abc/loader1.js?xyz!/abc/node_modules/loader2/index.js!/abc/resource.js?rrr'
resolve(context: string, request: string, callback: function(err, result: string))
解析像 require 表达式一样的请求。
context
必须是目录的绝对路径。此目录用作解析的起始位置。request
是要解析的请求。通常使用相对请求(例如 ./relative
)或模块请求(例如 module/path
),但绝对路径(例如 /some/path
)也可以作为请求。callback
是一个普通的 Node.js 风格回调函数,提供已解析的路径。解析操作的所有依赖项都会自动添加为当前模块的依赖项。
请求的资源部分,包括查询字符串。
在示例中:'/abc/resource.js?rrr'
资源文件。
在示例中:'/abc/resource.js'
资源的查询字符串。
在示例中:'?rrr'
从 webpack 4 开始,之前的 this.options.context
作为 this.rootContext
提供。
指示是否应生成源映射。由于生成源映射可能是一项耗时的任务,因此您应该检查是否确实请求了源映射。
编译目标。从配置选项传递。
示例值:'web'
、'node'
访问以下实用工具。
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;
};
加载器 API 版本。 当前为 2
。这对于提供向后兼容性很有用。通过此版本,您可以为重大更改指定自定义逻辑或回退。
当此项由 webpack 编译时,此布尔值设置为 true。
加载器接口提供所有模块相关信息。但在极少数情况下,您可能需要访问编译器 API 本身。
因此,您应该只将其作为最后手段使用。使用它们会降低加载器的可移植性。
访问 webpack 的当前 Compilation 对象。
访问 webpack 的当前 Compiler 对象。
一个布尔标志。在调试模式下设置。
从上一个加载器传递。如果您将输入参数作为模块执行,请考虑读取此变量以获取快捷方式(为了性能)。
指示结果是否应最小化。
将值传递给下一个加载器。如果您知道您的结果在作为模块执行时会导出什么,请在此处设置此值(作为仅包含一个元素的数组)。
对正在加载的 Module 对象的 hacky 访问。
您可以通过以下方式从加载器内部报告错误:
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
webpack v4 中引入了一种新的内联请求语法。在请求前加上 <match-resource>!=!
将为此请求设置 matchResource
。
当设置了 matchResource
时,它将用于与 module.rules
匹配,而不是原始资源。如果需要将更多加载器应用于资源,或者需要更改模块类型,这会很有用。它还会显示在 stats 中,并用于匹配 Rule.issuer
和 splitChunks
中的 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
的一部分导出通过引入 webpack 日志 API,我们希望统一 webpack 插件和加载器发出日志的方式,并允许以更好的方式检查构建问题。集成日志解决方案通过改善开发体验来支持插件和加载器开发人员。它为非 CLI 的 webpack 解决方案(如仪表板或其他 UI)铺平了道路。
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
示例可以看出,有两种类型的日志记录方法,
compilation.getLogger
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 仅旨在用作开发工具,不应包含在生产模式中。
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 打包您的应用程序时,您可以选择多种模块语法风格,包括 ES6、CommonJS 和 AMD。
虽然 webpack 支持多种模块语法,但我们建议遵循单一语法以保持一致性并避免奇怪的行为/错误。实际上,当 .mjs
文件、.cjs
文件或 .js
文件的最近父级 package.json
文件包含 "type"
字段且值为 "module"
或 "commonjs"
时,webpack 会强制执行此建议。请在继续阅读之前注意这些强制规定
.mjs
或 .js
,package.json
中带有 "type": "module"
require
、module.exports
或 exports
import './src/App.mjs'
而不是 import './src/App'
(您可以使用 Rule.resolve.fullySpecified
禁用此强制执行).cjs
或 .js
,package.json
中带有 "type": "commonjs"
import
和 export
都不可用.wasm
,package.json
中带有 "type": "module"
webpack 版本 2 原生支持 ES6 模块语法,这意味着您可以直接使用 import
和 export
,而无需像 babel 这样的工具来为您处理。请记住,您可能仍然需要 babel 来处理其他 ES6+ 特性。webpack 支持以下方法
静态地 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==';
将任何内容作为 default
或命名导出。
// Named exports
export var Count = 5;
export function Multiply(a, b) {
return a * b;
}
// Default export
export default {
// Some data...
};
function(string path):Promise
动态加载模块。对 import()
的调用被视为分割点,这意味着请求的模块及其子模块被分割成一个单独的 chunk。
if (module.hot) {
import('lodash').then((_) => {
// Do something with lodash (a.k.a '_')...
});
}
不可能使用完全动态的 import 语句,例如 import(foo)
。因为 foo
可能是您系统或项目中任何文件的任何路径。
import()
必须至少包含有关模块位置的一些信息。打包可以限制到特定目录或文件集,这样当您使用动态表达式时,import()
调用中可能请求的每个模块都将被包含。例如,import(`./locale/${language}.json`)
将导致 ./locale
目录中的每个 .json
文件都被打包到新的 chunk 中。在运行时,当变量 language
被计算后,任何文件(如 english.json
或 german.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');
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
);
}
新 chunk 的名称。从 webpack 2.6.0 开始,给定的字符串中支持占位符 [index]
和 [request]
,分别表示递增的数字或实际解析的文件名。添加此注释将使我们的独立 chunk 命名为 [my-chunk-name].js,而不是 [id].js。
为特定的动态导入设置 fetchPriority
。也可以通过使用 module.parser.javascript.dynamicImportFetchPriority
选项为所有动态导入设置全局默认值。
import(
/* webpackFetchPriority: "high" */
'path/to/module'
);
从 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 如何工作的更多信息,请查阅指南。
告诉浏览器当前导航可能需要该资源。有关 webpackPreload 如何工作的更多信息,请查阅指南。
一个正则表达式,将在导入解析期间进行匹配。只有匹配的模块才会被打包。
一个正则表达式,将在导入解析期间进行匹配。任何匹配的模块都不会被打包。
告诉 webpack 只打包动态 import()
导入模块的指定导出。这可以减小 chunk 的输出大小。自 webpack 5.0.0-beta.18 起可用。
CommonJS 的目标是为浏览器之外的 JavaScript 指定一个生态系统。webpack 支持以下 CommonJS 方法
require(dependency: String);
同步检索另一个模块的导出。编译器将确保依赖项在输出捆绑包中可用。
var $ = require('jquery');
var myModule = require('my-module');
require
也可以启用魔法注释,更多信息请参阅 module.parser.javascript.commonjsMagicComments
。
require.resolve(dependency: String);
同步检索模块的 ID。编译器将确保依赖项在输出捆绑包中可用。建议将其视为一个不透明的值,只能与 require.cache[id]
或 __webpack_require__(id)
一起使用(最好避免此类用法)。
有关更多信息,请参阅 module.id
。
对同一模块的多次 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(
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) 是一个 JavaScript 规范,它定义了用于编写和加载模块的接口。webpack 支持以下 AMD 方法
define([name: String], [dependencies: String[]], factoryMethod: function(...))
如果提供了 dependencies
,则 factoryMethod
将与每个依赖项的导出一起调用(按相同顺序)。如果未提供 dependencies
,则 factoryMethod
将与 require
、exports
和 module
一起调用(为了兼容性!)。如果此函数返回一个值,则该值由模块导出。编译器确保每个依赖项都可用。
define(['jquery', 'my-module'], function ($, myModule) {
// Do something with $ and myModule...
// Export a function
return function doSomething() {
// ...
};
});
define(value: !Function)
这将导出提供的 value
。这里的 value
可以是除函数之外的任何内容。
define({
answer: 42,
});
require(dependencies: String[], [callback: function(...)])
与 require.ensure
类似,这将把给定的 dependencies
分割成一个单独的 bundle,该 bundle 将异步加载。callback
将使用 dependencies
数组中每个依赖项的导出进行调用。
require(['b'], function (b) {
var c = require('c');
});
内部的 LabeledModulesPlugin
使您能够在模块内部使用以下方法进行导出和导入
导出给定的 value
。标签可以出现在函数声明或变量声明之前。函数名或变量名是导出值的标识符。
export: var answer = 42;
export: function method(value) {
// Do something...
};
使依赖项的所有导出在当前作用域中可用。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 特有的方法
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((dependency: String));
包含一个 dependency
而不执行它。这可用于优化模块在输出 chunk 中的位置。
require.include('a');
require.ensure(['a', 'b'], function (require) {
/* ... */
});
require.ensure(['a', 'c'], function (require) {
/* ... */
});
这将产生以下输出
file.js
和 a
b
c
如果没有 require.include('a')
,它将在两个匿名 chunk 中重复。
类似于 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
和其他变量访问编译过程中的某些数据。
如果模块正在执行,则为 false
;如果同步执行已完成,则为 true
。
指示是否启用热模块替换并提供进程接口。有关详细信息,请参阅HMR API 页面。
当前模块的 ID。
module.id === require.resolve('./file.js');
定义当消费者对模块进行 require
调用时将返回的值(默认为新对象)。
module.exports = function doSomething() {
// Do something...
};
此变量等于 module.exports
的默认值(即一个对象)。如果 module.exports
被覆盖,exports
将不再被导出。
exports.someValue = 42;
exports.anObject = {
x: 123,
};
exports.aFunction = function doSomething() {
// Do something
};
请参阅 node.js global。
出于兼容性原因,webpack 默认对 global
变量进行 polyfill。
取决于配置选项 node.__dirname
false
:未定义mock
:等于 '/'
true
:node.js __dirname如果在由解析器解析的表达式中使用,则配置选项被视为 true
。
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;
返回模块的绝对 file:
URL。
src/index.js
console.log(import.meta.url); // output something like `file:///path/to/your/project/src/index.js`
返回 webpack 版本。
src/index.js
console.log(import.meta.webpack); // output `5` for webpack 5
webpack 特有。是 module.hot
的别名,但 import.meta.webpackHot
可用于严格 ESM,而 module.hot
不能。
返回与 require.context
相同的值,但仅适用于 javascript/auto
和 javascript/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/,
});
取决于配置选项 node.__filename
false
:未定义mock
:等于 '/index.js'
true
:node.js __filename如果在由解析器解析的表达式中使用,则配置选项被视为 true
。
当前模块的资源查询字符串。如果进行了以下 require
调用,则查询字符串将在 file.js
中可用。
require('file.js?test');
file.js
__resourceQuery === '?test';
等于配置选项的 output.publicPath
。
原始的 require 函数。此表达式不会被解析器解析为依赖项。
内部 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
});
它提供对当前 module
的访问。在严格 ESM 中,module
不可用。
它提供对当前 module
的 ID (module.id
) 的访问。在严格 ESM 中,module
不可用。
访问所有模块的内部对象。
它提供对编译哈希的访问。
function (chunkId)
它根据 chunk 的 ID 提供 chunk 的文件名。
它是可赋值的,允许更改运行时使用的文件名。例如,它可用于在加载 chunk 时确定最终路径。
const oldFn = __webpack_get_script_filename__;
__webpack_get_script_filename__ = (chunkId) => {
const filename = oldFn(chunkId);
return filename + '.changed';
};
生成一个不由 webpack 解析的 require
函数。如果全局 require 函数可用,可用于实现一些很棒的功能。
在模块中,__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 打包。
if (__webpack_is_included__('./module-a.js')) {
// do something
}
在运行时更改基本 URI。
类型:string
可用版本:5.21.0+
示例
__webpack_base_uri__ = 'https://example.com';
访问当前入口点的运行时 ID。
这是 webpack 特有的功能,自 webpack 5.25.0 起可用。
src/index.js
console.log(__webpack_runtime_id__ === 'main');
等于配置选项 debug
。
Compiler
模块是主要引擎,它创建编译实例,并包含通过 CLI 或 Node API 传递的所有选项。它扩展了 Tapable
类,以便注册和调用插件。大多数面向用户的插件首先在 Compiler
上注册。
在为 webpack 开发插件时,您可能想知道每个 hook 在哪里被调用。要了解这一点,请在 webpack 源代码中搜索 hooks.<hook name>.call
。
Compiler
支持监视,它监视文件系统并在文件更改时重新编译。在监视模式下,编译器将发出额外的事件,例如 watchRun
、watchClose
和 invalid
。这通常用于开发中,通常在 webpack-dev-server
等工具的底层,这样开发人员就不必每次都手动重新编译。监视模式也可以通过 CLI 进入。
compiler
暴露了以下生命周期 hook,可以这样访问
compiler.hooks.someHook.tap('MyPlugin', (params) => {
/* ... */
});
根据 hook 类型,tapAsync
和 tapPromise
也可能可用。
有关 hook 类型的描述,请参阅Tapable 文档。
SyncHook
在准备编译器环境时调用,紧接在配置文件中初始化插件之后。
SyncHook
在 environment
hook 之后立即调用,当编译器环境设置完成时。
SyncBailHook
在处理 webpack 选项中的 entry
配置之后调用。
compiler.hooks.entryOption.tap('MyPlugin', (context, entry) => {
/* ... */
});
SyncHook
在设置完初始内部插件集后调用。
compiler
SyncHook
在解析器设置完成后触发。
compiler
SyncHook
当编译器对象被初始化时调用。
AsyncSeriesHook
在运行编译器之前添加一个 hook。
compiler
AsyncSeriesHook
在编译器开始读取 records
之前接入编译器。
compiler
AsyncSeriesHook
在监视模式下,当新的编译被触发但实际编译开始之前执行插件。
compiler
SyncHook
在创建 NormalModuleFactory 之后调用。
normalModuleFactory
SyncHook
在创建 ContextModuleFactory 之后运行插件。
contextModuleFactory
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();
});
SyncHook
在 beforeCompile
之后立即调用,在新编译创建之前。此 hook 不会复制到子编译器。
compilationParams
SyncHook
在初始化编译期间执行,在发出 compilation
事件之前。此 hook 不会复制到子编译器。
compilation
、compilationParams
SyncHook
在创建编译后运行插件。
compilation
、compilationParams
AsyncParallelHook
在完成编译之前执行。此 hook 不会复制到子编译器。
compilation
AsyncSeriesHook
在完成并封装编译后调用。
compilation
SyncBailHook
在发出资产之前调用。应返回一个布尔值,指示是否发出。
compilation
compiler.hooks.shouldEmit.tap('MyPlugin', (compilation) => {
// return true to emit the output, otherwise false
return true;
});
AsyncSeriesHook
在将资产发出到输出目录之前执行。此 hook 不会复制到子编译器。
compilation
AsyncSeriesHook
在将资产发出到输出目录后调用。此 hook 不会复制到子编译器。
compilation
AsyncSeriesHook
当资产已发出时执行。提供有关已发出资产的信息,例如其输出路径和字节内容。
file
、info
例如,您可以通过 info.content
访问资产的内容缓冲区
compiler.hooks.assetEmitted.tap(
'MyPlugin',
(file, { content, source, outputPath, compilation, targetPath }) => {
console.log(content); // <Buffer 66 6f 6f 62 61 72>
}
);
AsyncSeriesHook
当编译完成时执行。此 hook 不会复制到子编译器。
stats
AsyncSeriesHook
此 hook 允许您对构建进行一次额外的传递。
SyncHook
如果编译失败则调用。
error
SyncHook
当监视编译无效时执行。此 hook 不会复制到子编译器。
fileName
、changeTime
SyncHook
当监视编译停止时调用。
AsyncSeriesHook
当编译器关闭时调用。
SyncBailHook
当通过 infrastructureLogging
选项在配置中启用时,允许使用基础设施日志记录。
name
、type
、args
SyncBailHook
启用时允许记录到 stats 中,请参阅 stats.logging
、stats.loggingDebug
和 stats.loggingTrace
选项。
origin
、logEntry
Compilation
模块由 Compiler
用于创建新的编译(或构建)。compilation
实例可以访问所有模块及其依赖项(其中大多数是循环引用)。它是应用程序依赖关系图中所有模块的字面编译。在编译阶段,模块被加载、密封、优化、分块、哈希和恢复。
Compilation
类也扩展了 Tapable
并提供以下生命周期 hook。它们可以与编译器 hook 以相同的方式接入
compilation.hooks.someHook.tap(/* ... */);
与编译器一样,tapAsync
和 tapPromise
也可能可用,具体取决于钩子的类型。
SyncHook
在模块构建开始前触发,可用于修改模块。
module
compilation.hooks.buildModule.tap(
'SourceMapDevToolModuleOptionsPlugin',
(module) => {
module.useSourceMap = true;
}
);
SyncHook
在重建模块前触发。
module
SyncHook
在模块构建失败时运行。
module
error
SyncHook
在模块成功构建后执行。
module
AsyncSeriesHook
当所有模块都已构建且无错误时调用。
modules
SyncHook
当模块被重建时执行,无论成功或出错。
module
SyncHook
当编译停止接受新模块时触发。
SyncHook
当编译开始接受新模块时触发。
SyncBailHook
在依赖优化开始时触发。
modules
SyncHook
在依赖优化之后触发。
modules
SyncHook
afterChunks
钩子在 chunk 和模块图创建之后、优化之前调用。此钩子提供了检查、分析和在必要时修改 chunk 图的机会。
这是一个如何使用 compilation.hooks.afterChunks
钩子的示例。
chunks
SyncHook
在优化阶段开始时触发。
SyncBailHook
在模块优化阶段开始时调用。插件可以监听此钩子来对模块执行优化。
modules
SyncHook
在模块优化完成后调用。
modules
SyncBailHook
在 chunk 优化阶段开始时调用。插件可以监听此钩子来对 chunk 执行优化。
chunks
SyncHook
在 chunk 优化完成后触发。
chunks
AsyncSeriesHook
在优化依赖树之前调用。插件可以监听此钩子来执行依赖树优化。
chunks
modules
SyncHook
在依赖树优化成功完成后调用。
chunks
modules
SyncBailHook
在树优化之后,chunk 模块优化开始时调用。插件可以监听此钩子来对 chunk 模块执行优化。
chunks
modules
SyncHook
在 chunk 模块优化成功完成后调用。
chunks
modules
SyncBailHook
用于确定是否存储记录。返回任何 !== false
的值将阻止所有其他“记录”钩子(record
、recordModules
、recordChunks
和 recordHash
)的执行。
SyncHook
从记录中恢复模块信息。
modules
records
SyncHook
在为每个模块分配 id
之前执行。
modules
SyncHook
用于为每个模块分配 id
。
modules
SyncHook
在模块 id
优化开始时调用。
modules
SyncHook
在模块 id
优化阶段完成后调用。
modules
SyncHook
从记录中恢复 chunk 信息。
chunks
records
SyncHook
在为每个 chunk 分配 id
之前执行。
chunks
SyncHook
用于为每个 chunk 分配 id
。
chunks
SyncHook
在 chunk id
优化阶段开始时调用。
chunks
SyncHook
在 chunk id
优化完成后触发。
chunks
SyncHook
将模块信息存储到记录中。如果 shouldRecord
返回真值,则触发此操作。
modules
records
SyncHook
将 chunk 信息存储到记录中。仅当 shouldRecord
返回真值时才触发此操作。
chunks
records
SyncHook
在模块进行哈希处理之前调用。
syncHook
在模块进行哈希处理之后调用。
SyncHook
在编译进行哈希处理之前调用。
SyncHook
在编译进行哈希处理之后调用。
SyncHook
将记录哈希信息存储到 records
中。仅当 shouldRecord
返回真值时才触发此操作。
records
SyncHook
将 compilation
的信息存储到 records
中。仅当 shouldRecord
返回真值时才触发此操作。
compilation
records
SyncHook
在模块资源创建之前执行。
SyncHook
为 chunk 创建额外资源。
chunks
SyncBailHook
用于确定是否生成 chunk 资源。返回任何 !== false
的值将允许生成 chunk 资源。
SyncHook
在创建 chunk 资源之前执行。
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')
);
}
});
});
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();
}
);
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),
});
});
});
AsyncSeriesHook
优化存储在 compilation.assets
中的所有资源。
assets
SyncHook
资源已优化。
assets
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`);
});
}
);
除了 name
和 stage
,你还可以传入一个 additionalAssets
5.8.0+ 选项,它接受 true
值或一个以 assets
作为第一个参数的回调函数
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
}
);
(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"
});
}
);
SyncHook
在 processAssets
钩子无错误完成后调用。
SyncBailHook
用于确定编译是否需要解封以包含其他文件。
AsyncSeriesHook
在 needAdditionalSeal
之后立即执行。
SyncHook
触发以发出每个 chunk 的哈希值。
chunk
chunkHash
SyncHook
当模块中的资源被添加到编译时调用。
module
filename
SyncHook
当 chunk 中的资源被添加到编译时触发。
chunk
filename
SyncWaterfallHook
用于确定资源路径。
path
options
SyncBailHook
用于确定资源在发出后是否需要进一步处理。
SyncHook
在设置子编译器后执行。
childCompiler
compilerName
compilerIndex
从 webpack v5 开始,normalModuleLoader
钩子已被移除。现在要访问 loader,请改用 NormalModule.getCompilationHooks(compilation).loader
。
HookMap
此 HookMap
类似于一个操作列表,当使用预设时会被触发。它接收一个选项对象。当插件管理一个预设时,应小心地更改此对象中的设置,而不要替换现有设置。
options
context
这是一个说明性的插件示例
compilation.hooks.statsPreset.for('my-preset').tap('MyPlugin', (options) => {
if (options.all === undefined) options.all = true;
});
此插件确保对于预设 'my-preset'
,如果 all
选项未定义,则默认为 true
。
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
options
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);
});
});
HookMap
在每个级别上用结果调用。
result
context
此钩子提供了对特定选项的 StatsPrinter
类的访问。
statsPrinter
options
HookMap
当某个部分需要打印时调用此钩子。
object
context
HookMap
此钩子针对某个部分的结果字符串进行调用。
result
context
ContextModuleFactory
模块由 Compiler
使用,从 webpack 特定的 require.context
API 生成依赖。它解析请求的目录,为每个文件生成请求并根据传入的正则表达式进行过滤。匹配的依赖随后通过 NormalModuleFactory
。
ContextModuleFactory
类扩展了 Tapable
并提供了以下生命周期钩子。它们可以像编译器钩子一样被监听。
ContextModuleFactory.hooks.someHook.tap(/* ... */);
与编译器一样,tapAsync
和 tapPromise
也可能可用,具体取决于钩子的类型。
AsyncSeriesWaterfallHook
在解析请求的目录之前调用。可以通过返回 false
来忽略该请求。
data
AsyncSeriesWaterfallHook
在请求的目录解析完成后调用。
data
SyncWaterfallHook
在读取目录内容后调用。在递归模式下,也会为每个子目录调用。回调参数是每个目录中所有文件和文件夹名称的数组。
fileNames
AsyncSeriesWaterfallHook
在创建请求后但在根据正则表达式过滤之前,为每个文件调用。
request
options
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(/* ... */);
});
});
与编译器一样,tapAsync
和 tapPromise
也可能可用,具体取决于钩子的类型。
以下生命周期钩子由 parser
暴露,并可按如下方式访问
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;
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'
SyncBailHook
在评估作为自由变量的标识符时调用。
identifier
expression
SyncBailHook
在评估作为已定义变量的标识符时调用。
identifier
expression
SyncBailHook
在评估对已成功评估表达式的成员函数的调用时调用。
identifier
expression
param
此表达式将触发钩子
index.js
const a = expression.myFunc();
MyPlugin.js
parser.hooks.evaluateCallExpressionMember
.for('myFunc')
.tap('MyPlugin', (expression, param) => {
/* ... */
return expressionResult;
});
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'
SyncBailHook
在解析 if
语句时调用。与 statement
钩子相同,但仅在 statement.type == 'IfStatement'
时触发。
statement
SyncBailHook
在解析带有标签的语句时调用。这些语句的 statement.type === 'LabeledStatement'
。
labelName
statement
SyncBailHook
为代码片段中的每个 import
语句调用。source
参数包含导入文件的名称。
statement
source
以下 import
语句将触发该钩子一次
index.js
import _ from 'lodash';
MyPlugin.js
parser.hooks.import.tap('MyPlugin', (statement, source) => {
// source == 'lodash'
});
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'
*/
}
);
SyncBailHook
为代码片段中的每个 export
语句调用。
statement
SyncBailHook
为每个 export-import
语句调用,例如:export * from 'otherModule';
。
statement
source
SyncBailHook
为每个导出声明的 export
语句调用。
statement
declaration
这些 export 将触发此钩子
export const myVar = 'hello'; // also var, let
export function FunctionName() {}
export class ClassName {}
SyncBailHook
为每个导出表达式的 export
语句调用,例如 export default expression;
。
statement
declaration
SyncBailHook
为每个 export
语句的每个 specifier 调用。
statement
identifierName
exportName
index
SyncBailHook
为每个 export-import
语句的每个 specifier 调用。
statement
source
identifierName
exportName
index
SyncBailHook
在解析变量声明时调用。
declaration
SyncBailHook
在解析使用 let
定义的变量声明时调用
declaration
SyncBailHook
在解析使用 const
定义的变量声明时调用
declaration
SyncBailHook
在解析使用 var
定义的变量声明时调用
declaration
SyncBailHook
在重命名标识符之前触发,以确定是否允许重命名。通常与 rename
钩子一起使用。
identifier
expression
var a = b;
parser.hooks.canRename.for('b').tap('MyPlugin', (expression) => {
// returning true allows renaming
return true;
});
SyncBailHook
在重命名时触发,以获取新的标识符。仅当 canRename
返回 true
时才调用此钩子。
identifier
expression
var a = b;
parser.hooks.rename.for('b').tap('MyPlugin', (expression) => {});
SyncBailHook
在解析 AssignmentExpression
并在解析赋值表达式之前调用。
identifier
expression
a += b;
parser.hooks.assigned.for('a').tap('MyPlugin', (expression) => {
// this is called before parsing b
});
SyncBailHook
在解析 AssignmentExpression
并在解析赋值表达式之前调用。
identifier
expression
a += b;
parser.hooks.assigned.for('a').tap('MyPlugin', (expression) => {
// this is called before parsing a
});
SyncBailHook
在解析标识符的 typeof
时触发
identifier
expression
SyncBailHook
在解析函数调用时调用。
identifier
expression
eval(/* something */);
parser.hooks.call.for('eval').tap('MyPlugin', (expression) => {});
SyncBailHook
在解析对对象成员函数的调用时触发。
objectIdentifier
expression, properties
myObj.anyFunc();
parser.hooks.callMemberChain
.for('myObj')
.tap('MyPlugin', (expression, properties) => {});
SyncBailHook
在解析 new
表达式时调用。
identifier
expression
new MyClass();
parser.hooks.new.for('MyClass').tap('MyPlugin', (expression) => {});
SyncBailHook
在解析表达式时调用。
identifier
expression
const a = this;
parser.hooks.expression.for('this').tap('MyPlugin', (expression) => {});
SyncBailHook
在解析 ConditionalExpression
时调用,例如 condition ? a : b
expression
SyncBailHook
获取代码片段的抽象语法树(AST)
ast
comments
NormalModuleFactory
模块由 Compiler
使用,用于生成模块。从入口点开始,它解析每个请求,解析内容以查找更多请求,并通过解析所有新文件和解析任何新文件来持续遍历文件。在最后阶段,每个依赖都成为一个 Module
实例。
NormalModuleFactory
类扩展了 Tapable
并提供了以下生命周期钩子。它们可以像编译器钩子一样被监听。
NormalModuleFactory.hooks.someHook.tap(/* ... */);
NormalModuleFactory
创建 Parser
和 Generator
实例,这些实例可以通过 HookMaps
访问。必须传递标识符才能监听这些钩子。
NormalModuleFactory.hooks.someHook.for('identifier').tap(/* ... */);
与编译器一样,tapAsync
和 tapPromise
也可能可用,具体取决于钩子的类型。
AsyncSeriesBailHook
在遇到新的依赖请求时调用。可以通过返回 false
来忽略依赖。否则,应返回 undefined
以继续。
resolveData
AsyncSeriesBailHook
在开始解析之前调用。它应该返回 undefined
以继续。
resolveData
AsyncSeriesBailHook
在请求解析之前调用。可以通过返回 false
来忽略依赖。返回一个 Module
实例将终止该过程。否则,应返回 undefined
以继续。
resolveData
AsyncSeriesBailHook
在解析带有方案(URI)的请求之前调用。
resolveData
AsyncSeriesBailHook
在请求解析完成后调用。
resolveData
AsyncSeriesBailHook
在创建 NormalModule
实例之前调用。
createData
resolveData
HookMap<SyncBailHook>
一个钩子,允许您在创建模块时覆盖 NormalModule
类。此钩子在 createModule
钩子之后和 module
钩子之前调用。
钩子参数: identifier
回调参数:createData
resolveData
SyncWaterfallHook
在创建 NormalModule
实例之后调用。
module
createData
resolveData
HookMap<SyncBailHook>
在创建 Parser
实例之前调用。parserOptions
是 module.parser
中对应标识符的选项,或一个空对象。
钩子参数: identifier
回调参数:parserOptions
HookMap<SyncHook>
在创建 Parser
实例后触发。
钩子参数: identifier
回调参数:parser
parserOptions
可能的默认标识符
javascript/auto
javascript/dynamic
javascript/esm
JSON
webassembly/sync
webassembly/async
asset
HookMap<SyncBailHook>
在创建 Generator
实例之前调用。generatorOptions
是 module.parser
中对应标识符的选项,或一个空对象。
钩子参数: identifier
回调参数:generatorOptions
HookMap<SyncHook>
在创建 Generator
实例之后调用。
钩子参数: identifier
回调参数:generator
generatorOptions
可能的默认标识符
JSON
webassembly/sync
webassembly/async
asset
asset/source
asset/resource
asset/inline
Compilation
对象有许多可用的方法和钩子。在此页面,我们将列出可用的方法和属性。
function
返回当前编译的 Stats
对象。
function (module, callback)
向当前编译添加模块。
参数
module
- 要添加的模块callback
- 模块添加后的回调函数function (module)
根据标识符从编译中获取模块。
参数
module
- 要获取的模块。标识符通过 compilation
使用 module.identifier()
方法从模块中提取。function (module)
尝试按标识符搜索模块。
参数
module
- 要搜索的模块。标识符通过 compilation
使用 module.identifier()
方法从模块中提取。function (module, optional, origin, dependencies)
构建给定模块。
参数
module
- 要构建的模块。optional
- 可选标志。origin
- 请求此模块构建的原始模块。dependencies
- 要构建模块的可选依赖项。function (module, callback)
处理给定模块的依赖项。
参数
module
- 要处理其依赖项的模块。callback
- 模块依赖项处理完成后要调用的函数。function (context, entry, name, callback)
向编译添加一个入口。
参数
context
- 入口上下文路径。entry
- 入口依赖项。name
- 入口的名称。callback
- addEntry
完成后要调用的函数。function (module, thisCallback)
触发模块的重建。
参数
module
- 要重建的模块。thisCallback
- 模块重建完成后要调用的函数。function (callback)
完成编译并调用给定回调。
参数
callback
- 编译完成后要调用的函数。function (callback)
密封编译。
参数
callback
- 编译密封完成后要调用的函数。function
解封编译。
参数
callback
- 编译解封完成后要调用的函数。function (module, blocks)
将给定模块的错误和警告添加到编译的错误和警告中。
参数
module
- 要报告其错误和警告的模块。blocks
- 一组要报告的依赖块。function (groupOptions, module, loc, request)
将模块添加到现有 chunk 组或创建新组。返回一个 chunkGroup
。
参数
groupOptions
- chunk 组的选项。module
- 引用 chunk 组的模块。loc
- 引用 chunk 组的位置(模块内部)。request
- 引用 chunk 组的请求。function (name)
创建新 chunk 并将其添加到 compilation.chunks
。返回该 chunk
。
参数
name
- chunk 的名称。function (module)
递归地为给定模块及其依赖块分配 depth
。
参数
module
- 要分配深度的模块。function (module, dependency)
从给定模块返回对依赖项的引用。
参数
module
- 相关模块。dependency
- 要获取引用的依赖项。function (inputChunkGroups)
从 Module
图创建 Chunk
图。该过程分两个阶段完成。第一阶段:遍历模块图并在 chunkDependencies
中构建基本的 chunk 图。第二阶段:遍历基本 chunk 图的每种可能方式并跟踪可用模块。在遍历过程中,processDependenciesBlocksForChunkGroups
将 chunk 相互连接,并将 Blocks
与 Chunks
连接。当 chunk 的所有模块都已可用时,它会停止遍历,并且不会连接不需要的 chunk。
参数
inputChunkGroups
- 正在处理的 chunk 组。function (module, block)
移除模块与依赖块的关系。
参数
module
- 要移除模块关系。block
- 依赖块。function (module, chunk)
在移除依赖原因后,修补模块和 chunk 的关联。由 removeReasonsOfDependencyBlock
自动调用。
参数
module
- 要修补关联的模块。chunk
- 要修补关联的 chunk。function (block, chunk)
在移除依赖原因后,从依赖块模块和 chunk 中移除给定 chunk。由 removeReasonsOfDependencyBlock
自动调用。
参数
block
- Chunk
的块关联。chunk
- 要从依赖项中移除的 chunk。function
function
function
function
function
function (filename, data)
返回插值路径。
参数
filename
- 用于获取带哈希的资源路径。data
- 数据对象。function (filename, data)
返回插值路径和资源信息。
参数
filename
- 用于获取带哈希的资源路径。data
- 数据对象。function (name, outputOptions, plugins)
允许在 webpack 内部运行另一个 webpack 实例。但是,作为子实例,它具有不同的设置和配置。它从父(或顶级)编译器复制所有钩子和插件,并创建一个子 Compiler
实例。返回创建的 Compiler
。
参数
name
- 子 Compiler
的名称。outputOptions
- 输出选项对象。plugins
- 将应用的 webpack 插件。function
function (file, source, assetInfo = {})
参数
file
- 资源的 文件名source
- 资源的 源assetInfo
- 额外资源信息function (file, newSourceOrFunction, assetInfoUpdateOrFunction)
参数
file
- 资源的 文件名newSourceOrFunction
- 新资源源或将旧源转换为新源的函数assetInfoUpdateOrFunction
- 新资源信息或将旧信息转换为新信息的函数function (file)
参数
file
- 资源的 文件名function
返回当前编译下所有资源的数组。
function (name)
参数
name
- 要返回的资源名称插件是 webpack 生态系统的关键组成部分,为社区提供了一种强大的方式来深入 webpack 的编译过程。插件能够钩入在每次编译过程中触发的关键事件。在每一步,插件都将完全访问 compiler
,并在适用时访问当前 compilation
。
我们首先介绍 tapable
工具,它提供了 webpack 插件接口的骨干。
这个小型库是 webpack 中的一个核心工具,但也可以在其他地方用于提供类似的插件接口。webpack 中的许多对象都扩展了 Tapable
类。该类暴露了 tap
、tapAsync
和 tapPromise
方法,插件可以使用这些方法注入在编译过程中触发的自定义构建步骤。
请参阅文档了解更多信息。理解这三个 tap
方法以及提供它们的钩子至关重要。扩展 Tapable
的对象(例如 compiler
)、它们提供的钩子以及每个钩子的类型(例如 SyncHook
)都将被注明。
根据所使用的钩子和应用的 tap
方法,插件可以以不同的方式运行。其工作原理与 Tapable
提供的钩子密切相关。编译器钩子会注明底层 Tapable
钩子,指示哪些 tap
方法可用。
因此,根据您监听的事件,插件可能会以不同的方式运行。例如,当钩入 compile
阶段时,只能使用同步的 tap
方法
compiler.hooks.compile.tap('MyPlugin', (params) => {
console.log('Synchronously tapping the compile hook.');
});
但是,对于使用 AsyncHook
的 run
钩子,我们可以使用 tapAsync
或 tapPromise
(以及 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
,每种方法都允许您的插件以其认为合适的方式运行。
为了向编译提供自定义钩子以供其他插件监听,您需要执行以下操作:
为编译钩子创建模块作用域的 WeakMap
const compilationHooks = new WeakMap<Compilation, MyHooks>();
interface MyHooks {
custom: SyncHook<[number, string]>;
}
在您的插件上创建静态方法
static getCompilationHooks(compilation: Compilation) : MyHooks {
let hooks = compilationHooks.get(compilation);
if(hooks === undefined) {
compilationHooks.set(compilation, hooks = {
custom: new SyncHook()
});
}
return hooks;
}
在您的插件中像下面这样调用钩子
const hooks = MyPlugin.getCompilationHooks(compilation);
hooks.custom.call(1, 'hello');
其他插件也可以访问您的自定义钩子
import MyPlugin from 'my-plugin';
const hooks = MyPlugin.getCompilationHooks(compilation);
hooks.custom.tap('OtherPlugin', (n, s) => {
// magic
});
再次强调,请参阅 tapable
文档以了解不同钩子类及其工作原理的更多信息。
插件可以通过 ProgressPlugin
报告进度,该插件默认将进度消息打印到标准错误输出。要启用进度报告,在运行 webpack CLI 时传递 --progress
参数。
可以通过向 ProgressPlugin
的 reportProgress
函数传递不同的参数来自定义打印输出。
要报告进度,插件必须使用 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-resolve
和 tapable
文档。
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
文档,了解钩子的完整列表及其描述。
上述解析器也可以通过配置文件中的 resolve
或 resolveLoader
选项进行自定义。这些选项允许用户通过包括解析插件在内的各种选项来更改解析行为。
解析器插件(例如 DirectoryNamedPlugin
)可以直接包含在 resolve.plugins
中,而不是直接在 plugins
配置选项中使用。