Webpack 5.0 正式发布:更好的持久化缓存算法、提高 Web 平台的兼容性、带来 Node 生态新功能

  • 2020-11-30
  • 本文字数:19019 字

    阅读完需:约 62 分钟

时隔两年,webpack 5 正式发布,快来看看有哪些新特性吧!(建议收藏)

webpack 4 于 2018 年 2 月发布,之后到现在虽然更新了很多功能,但没有重大更改。我们知道开发者不喜欢带很多重大更改的大版本。尤其是使用 webpack 时,开发者通常每年只“碰”它两次,而剩下的时间 webpack “就在那儿工作”。但是交付功能时不做重大更改是需要付出代价的:我们无法进行主要的 API 或架构改进。

该来的总是要来的,为了避免混乱,我们被迫进行重大更改。而现在是时候推出新的主要版本了。因此 webpack 5 包含了一众架构改进和得益于新架构的特性。

这个新的大版本还让我们有机会修改一些默认设置,并与近期新提出的提案和规范保持一致。

所以 webpack 5.0.0 发布了,但这并不意味着它已经完成、没有错误,甚至也不等于功能都完善好了。与 webpack 4 一样,我们会继续开发下去,不断修复问题和添加功能。在接下来的几天中可能会有很多错误修正。还有一些功能将稍后发布。

常见问题

新版正式发布意味着什么?

这意味着我们完成了重大更改。我们进行了许多重构,以升级架构并为将来的功能(和当前功能)奠定良好的基础。

那么什么时候升级呢?

看具体情况。升级很有可能失败,你可能要试两三次。如果你愿意这样做,请立即尝试升级并向 webpack、插件和加载程序提供反馈。我们希望尽快解决这些问题。总有人要迈出第一步,你将是最早从中受益的人之一。

总体方向

此版本重点关注以下内容:

  • 通过持久缓存提高构建性能。

  • 使用更好的算法和默认值来改善长期缓存。

  • 通过更好的 Tree Shacking 和代码生成来改善包大小。

  • 改善与 web 平台的兼容性。

  • 在 v4 中实施的一些功能没有引入重大更改,导致了一些怪异的内部结构,现在要清理掉。

  • 现在就引入一些重大更改为将来的功能做准备,使我们能够尽可能长时间地停留在 v5 上。

迁移指南

详见《迁移指南》

重大更改:移除

移除已弃用的项目

v4 中弃用的所有项目均已移除。

迁移:确保你的项目不包含 webpack 4 中已经 Warning 的功能。

以下是一些已移除但在 v4 中没有弃用警告的内容:

  • 现在必须只传递一个参数(可以是对象、字符串或函数)给 IgnorePlugin 和 BannerPlugin。

弃用代码

新的弃用包括弃用代码,因此更易于引用。

已弃用语法

require.include 已弃用,使用时默认会发出警告。

可以使用 Rule.parser.requireInclude 将行为更改为 allowed, deprecated 或 disabled。

移除自动化 Node.js Polyfills

早期,webpack 的目标是允许在浏览器中运行大多数 Node.js 模块,但是模块格局已经发生了很大变化,现在许多模块主要是出于前端目的编写的。webpack<=4 附带了许多 Node.js 核心模块的 polyfills,一旦一个模块使用任何核心模块(如 crypto 模块),这些 polyfills 就会自动应用。

虽然这让为 Node.js 编写的模块用起来更方便,但它会将这些巨大的 polyfill 添加到包中。在许多情况下,这些 polyfills 是不必要的。

webpack 5 会停止自动 polyfilling 这些核心模块,并专注于与前端兼容的模块。我们的目标是提高与 web 平台的兼容性,而在 web 平台上 Node.js 核心模块不可用。

迁移:

  • 尽可能尝试使用与前端兼容的模块。

  • 可以为 Node.js 核心模块手动添加 polyfill。错误消息将提示实现方法。

  • 包作者:使用 package.json 中的 browser 字段使包与前端兼容。提供浏览器的替代实现 / 依赖。

主要更改:长期缓存

确定性块、模块 ID 和导出名称

新版添加了用于长期缓存的一些新算法,在生产模式下默认启用。

chunkIds: "deterministic" moduleIds: "deterministic" mangleExports: "deterministic"

该算法以确定性方式为模块和块分配短(3 或 5 位数字)数字 ID,并为导出分配短(2 个字符)名称。这是包大小和长期缓存之间的折衷方案。

moduleIds/chunkIds/mangleExports: false 禁用默认行为,并且可以通过插件提供自定义算法。请注意,在 webpack 4 中,moduleIds/chunkIds: false(不带自定义插件)会正常构建,而在 webpack 5 中你必须提供自定义插件。

迁移:最好使用 chunkIds、moduleIds 和 mangleExports 的默认值。你还可以选择使用旧的默认 chunkIds: "size", moduleIds: "size", mangleExports: "size",这将生成较小的包,但会为了缓存而更频繁使它们无效化。

注意:在 webpack 4 中,哈希过的模块 ID 导致 gzip 性能降低。这与更改的模块顺序有关,并且已得到修复。

注意:在 webpack 5 中,默认情况下在生产模式下启用 deterministic ID。

真实内容哈希

现在,当使用 [contenthash] 时,webpack 5 将使用文件内容的真实哈希。在此之前“仅”使用内部结构的哈希。当仅更改注释或重命名变量时,这可能对长期缓存产生积极影响。包缩小后,这些更改不可见。

重大更改:开发支持

命名块 ID

在开发模式下默认启用的新命名块 ID(named chunk id)算法为块(和文件名)提供了易于理解的名称。模块 ID 由其相对于 context 的路径确定。块 ID 由块的内容确定。

因此,你不再需要使用 import(/* webpackChunkName: "name" */ "module") 进行调试。但如果要控制生产环境的文件名,这还是有用的。

可以在生产环境中使用 chunkIds:"named",但请确保不要意外地泄露有关模块名称的敏感信息。

迁移:如果你不喜欢在开发中更改文件名,则可以传递 chunkIds:"natural"以使用旧的数字模式。

模块联盟

webpack 5 添加了一个称为“模块联盟”(Module Federation)的新功能,允许多个 webpack 构建协同工作。从运行时的角度来看,来自多个构建的模块的行为就像一个巨大的互联模块图。从开发人员的角度来看,可以从指定的远程构建中导入模块,并以最少的限制使用它们。

更多详细信息请见该指南

重大更改:新的 Web 平台特性

JSON 模块

JSON 模块现在与提案保持一致,并在使用非默认导出时发出警告。从严格的 ECMAScript 模块导入时,JSON 模块不再有命名导出。

迁移:使用默认导出。

即使在使用默认导出时,optimization.usedExports 优化也会移除未使用的属性,而 optimization.mangleExports 优化会破坏属性。

可以在 Rule.parser.parse 中指定自定义 JSON 解析器,以导入类似 JSON 的文件(例如 toml、yaml、json5 等)。

import.meta

  • import.meta.webpackHot 是 module.hot 的别名,在严格的 ESM 中也可用

  • import.meta.webpack 是 webpack 的主要版本,数字表示

  • import.meta.url 是当前文件的 file: url(类似 __filename,但作为文件 url)

资产模块

webpack 5 现在对表示资产的模块提供原生支持。这些模块会将文件发射到输出文件夹中,或者将 DataURI 注入到 JavaScript 包中。无论哪种方式,它们都提供了可使用的 URL。

它们可以通过多种方式使用:

  • 匹配此类导入时,import url from "./image.png",并在 module.rules 中设置 type: "asset"。(旧方法)

  • new URL("./image.png", import.meta.url)(新方法)

新方法也允许运行没有打包程序的代码。此语法在浏览器的原生 ECMAScript 模块中也可用。

原生 Worker 支持

将资产的 new URL 与 new Worker/newSharedWorker/navigator.serviceWorker.register 结合使用时,webpack 会自动为 webWorker 创建一个新的入口点。

new Worker(new URL("./worker.js", import.meta.url))

选择该语法以允许运行代码时无需打包程序。此语法在浏览器的原生 ECMAScript 模块中也可用。

URIs

webpack 5 支持处理请求中的协议。

  • 支持 data: 。支持 Base64 或原始编码。Mimetype 可以映射到 module.rules 中的加载程序和模块类型。示例:import x from "data:text/javascript,export default 42"

  • 支持 file: 。

  • 支持 http(s): ,但需要通过 new webpack.experiments.schemesHttp(s)UriPlugin() 启用

  • 默认情况下,定位“web”时,这些 URI 导致对外部资源的请求(它们是外部资源)

支持请求中的 fragment,示例:./file.js#fragment

Async 模块

webpack 5 支持所谓的“async 模块”。这些模块不会同步执行,而是异步和基于 Promise 的模块。

通过 import 可以自动导入它们,并且不需要其他语法,和之前几乎没有差异。

通过 require() 导入它们将返回一个解析为导出的 Promise。

在 webpack 中,有多种方式来拥有 async 模块:

  • 异步 externals

  • 新规范中的 WebAssembly 模块

  • 使用顶级 Await 的 ECMAScript 模块

externals

webpack 5 添加了更多外部类型以覆盖更多应用程序:

promise:表示为 Promise 的表达式。该外部模块是 async 模块,解析值用作模块导出。

import:原生 import() 用于加载指定的请求。该外部模块是 async 模块。

module:尚未实现,但计划通过 import x from "..."加载模块。

script:通过