Webpack 5.111
Webpack 5.111 来了!这个版本帮你省去了几件麻烦事:复制公共文件、为旧浏览器准备 CSS、找出不再需要打包的代码。ES module 输出也已转正,你可以从配置中移除实验性开关了。
此外还有更精简的 chunk 加载运行时、减少两个依赖、修复热更新和缓存问题,并为插件作者提供了一些新工具。下面来看看这次更新对项目有什么影响,以及从 5.110 升级需要改什么。
看看新内容:
- ES Module 输出无需实验开关
- ES Module 库与 Externals
- 配置中的 File URL
- 复制静态文件
- 面向目标浏览器的 CSS
- HTML 与内嵌内容
- 更实用的性能提示
- 更小的产物与更少的内存占用
- Webpack 自研 JavaScript 解析器
- 可选缓存依赖
- Module Namespace 对象
- 其他改进
- Bug 修复
ES Module 输出无需实验开关
以 ES module 形式发布应用或库?现在可以移除 experiments.outputModule,保留 output.module 即可。ESM 输出已经稳定,module 和 modern-module 库类型以及 externalsType: "module" 也无需实验开关就能使用:
export default {
- experiments: {
- outputModule: true,
- },
output: {
module: true,
},
};
升级时需注意一个细节:如果仅设置了 experiments.outputModule: true,请补充 output.module: true。 Webpack 现已忽略旧版标志,仅保留它无法启用 ESM 输出。universal、deno 和 bun 目标仍默认使用 ESM;其他目标仍默认使用经典输出。
现在,将库类型设为 module 或 modern-module 会自动开启 output.module。此类库仅以 ECMAScript 模块形式存在,因此无需重复配置:
export default {
output: {
library: {
type: "module",
},
},
};
希望立即体验 Webpack 6 的默认行为?experiments.futureDefaults 现已包含 output.module。只要target支持,构建即可输出 ECMAScript 模块。以下三种情况仍会保持经典脚本输出:目标明确声明不支持模块(例如解析到 ie 11 或更低版本的 node10 等 browserslist 查询结果)、库类型为从脚本中读取的 var 或 umd,以及手动设置 output.module(该设置在双向中均优先)。
异步启动也得到改进。若入口使用顶层 await 加载配置,导入你打包代码的下游将等待该设置完成,失败时会接收到错误。此功能要求目标支持顶层 await。Webpack 会自动检测该支持,你也可在输出环境设置中通过 output.environment.topLevelAwait 进行覆盖。
ES 模块库与外部依赖
如果你发布的 ES 模块库重新导出了保持为外部的包,现在多项行为已修正:
Copyexport * from "lodash";
export { render } from "react-dom";
- 实时绑定得以保留。由于 Webpack 发出的是原生重新导出而非一次性拷贝,外部包后续重新赋值的变量可被消费者看到。
externals映射会被采用。 使用星号重新导出(star re-export)时,输出的请求将使用你配置的路径,而非源码中原始书写的路径。- 保留导入属性(Import attributes)。因此,像
export * from "./data.json" with { type: "json" }这样的语句,其属性在输出的代码中会被保留。 - 循环依赖不再导致栈溢出。此前,当两个模块通过外部模块(external)互相进行星号重新导出时,会引发此问题。
- 对说明符(Specifiers)进行转义,使得包含特殊字符的请求在输出时能生成合法的字符串字面量。
现在,一种此前会被错误输出的情况会作为报告显示出来:声明为数组的外部依赖(external)实际上是一个模块说明符加上一个属性路径。从该外部依赖执行
export * from时,Webpack 之前会重新导出整个模块,而非你指定的那个属性。现在 Webpack 会将其报告为构建错误,以便你明确指定想要导出的项。配置中的文件 URL
如果你将 Webpack 配置编写为 ES 模块,现在可以直接向
context和output.path等路径选项传递file:URL 字符串。这省去了额外使用fileURLToPath()进行转换的步骤:复制代码块:
-import { fileURLToPath } from "node:url";
-
export default {
output: {
- path: fileURLToPath(new URL("./dist/", import.meta.url)),
+ path: new URL("./dist/", import.meta.url).href,
},
};
你可以对
test和include等规则条件使用相同的方法。请记住使用.href:在此处,Webpack 期望接收的是 URL 字符串,而不是URL对象。复制静态文件
即使没有任何文件导入它们,你的
robots.txt、公共图片以及许可证文件也需要进入输出目录。借助output.copy,你可以要求 Webpack 在构建过程中复制整个文件夹:复制代码块:
export default {
output: {
copy: "public",
},
};
这会将
public/的内容复制到你的输出目录中,并保留原有的文件夹结构。你不再需要为这种设置单独执行复制步骤。在监视模式(watch mode)运行期间编辑文件时,Webpack 会自动为你复制更新后的版本。这些文件还会出现在构建统计信息(build stats)中,并且在output.clean清理输出目录时会被保留。
若需从多个位置复制文件,或指定文件的输出路径,可使用模式配置:
Copyexport default {
output: {
copy: [
"public",
{ from: ["licenses/*.txt", "vendor/licenses/*.txt"], to: "licenses" },
{ from: "images", to: "img", filename: "[name].[contenthash][ext]" },
],
},
};
你还可以筛选文件、转换内容,或保留文件权限和时间戳。Webpack 会在模式匹配无结果时发出警告;若复制操作会覆盖已生成的资源,则直接报错,助你尽早发现路径错误。如需更精细地控制复制时机及并发处理文件数,请使用内置的 CopyPlugin。
针对浏览器目标的 CSS 处理
你可以编写现代 CSS,Webpack 负责适配旧版浏览器。使用 browserslist 目标时,内置的 CSS 压缩器会检测你支持的浏览器范围。lowerUnsupported 会将较新语法中受支持的部分改写为旧浏览器可识别的形式;colorFallbacks 则在新颜色声明前添加兼容性声明。
这两项功能默认开启。此版本增强了语法覆盖范围,包括 inset、媒体查询范围以及 CSS 嵌套,并优化了压缩器对数学表达式和颜色的简化逻辑。你可以通过 optimization.minimize.css 调整具体设置:
export default {
target: "browserslist",
optimization: {
minimize: {
css: {
lowerUnsupported: true,
colorFallbacks: true,
},
},
},
};
举例来说,如果目标浏览器列表中的某款浏览器不支持 inset,Webpack 会自动将其转换为四边分别指定的写法。以下是转换后的代码(已格式化以便阅读):
.panel {
- inset: 0;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
}
如果你的项目需要更多控制权,可以启用以下选项:
unusedSymbols允许移除你标记为未使用的类名、ID、关键帧和自定义属性。你需要在检查项目后提供具体名称,Webpack 不会自动识别这些未使用的符号。pseudoClasses选项适用于你借助脚本实现伪类行为的场景。例如,配置{ "focus-visible": "focus-visible" }会将:focus-visible替换为.focus-visible,你可以用脚本动态添加这个类名。resolveCustomAtRules允许压缩器处理未经过 Webpack CSS 解析器的样式表中的@custom-media和@custom-selector。rewriteDirSelector会将不支持的:dir()重写为基于属性选择器的近似写法。仅当这种近似满足你的样式需求时才启用。mergeDistantRules即使中间隔着其他规则,也会合并声明一致的规则。该选项默认关闭,因为改变规则顺序可能会影响样式优先级。
压缩功能现在更仔细地保留颜色值,特别是在其他 CSS 表达式读取颜色通道时;同时也确保 CSS 层级块的顺序正确。这些优化有助于生产环境的样式表现与你编写时一致。
关于 CSS Modules,还有一个值得关注的修复:在 at-rule 前置部分(如 @keyframes 名称)中使用的 @value,现在只会进行作用域隔离一次。此前,通用的值处理逻辑会二次重写它,导致生成了动画无法匹配的双重命名。
HTML 与嵌入内容
如果你使用 Webpack 原生的 HTML 支持,本次发布进一步优化了生成的页面。压缩器会去除属性值中 URL 周围的空白,例如 href 和 src。你还可以通过 mergeScripts 合并相邻的裸 script 标签,或通过 collapseBooleanAttributes: "all" 简化更多布尔属性。具体设置请参阅 optimization.minimize.html。
属性压缩现在依据属性值的语义而非源码中的拼写方式。过去,带引号的值会被重写,而不带引号的值则按原样保留,导致 method="GET" 被简化而 method=GET 没有。现在两者走相同的压缩路径,包括布尔属性的简化(