Webpack 5 released,中文(部分)尝鲜版介绍

2,000 阅读7分钟

前言

就在昨天,webpack 5 released ,我也火急火燎地去看了一下更新文档介绍。确实,如文档开头所说这次做了重大改变,以便于未来可以很好地应用新的功能。因此,整个发布内容的介绍非常多,大概 1 万多字。并且兴趣使然,我也开始了火急火燎的翻译,但出于个人时间和信息的时效性考虑,所以先把翻译的部分内容发布出来。

英文版,刚刚印记中文也发布了 中文版

正文开始

webpack 4 于 2018 年 2 月发布。从那时起,我们发布了许多没有进行重大改变的功能。我们知道大家不喜欢经常性地重大更改。尤其是使用 webpack 时,大家通常每年只接触两次,而剩下的时间可以正常工作就行。但是,在不重大改变的情况下交付功能也需要付出代价:我们无法进行主要的 API 或体系结构的改进。

因此,有时会遇到很多困难,所以,为了避免造成混乱,我们被迫进行了重大更改。现在是时候推出新的主要版本了。因此,webpack 5 包含了这些体系结构改进和功能,如果没有它们就无法实现这个版本。

主要版本还提供了修改一些默认设置以及与同时提出的建议和规范保持一致的机会。

所以,今天(2020-1010)webpack 5 发布了,但这并不是意味着它已经完成,没有 Bug 或功能已完善。与 webpack 4 一样,我们会通过修复 Bug 和添加功能来持续优化。在接下来的几天中,可能会有很多 Bug 被修复。而功能将会稍后发布。

常见问题

所以,这个发布的意义是什么?

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

所以,什么时候升级?

这会由很多因素决定。升级可能会失败,你可能需要重新尝试第二遍或第三遍。如果你对此持开放态度,可以现在尝试升级并且给我们提供反馈,例如 plugins 和 loaders。我们渴望解决掉这些问题。当你开始使用了,你也将是最早从中受益的人之一。

大致的方向

本次发布重点关注以下内容:

  • 通过持续缓存提高构建性能
  • 使用更好的算法和默认值来改善长期缓存
  • 通过更好地 Tree Shaking 和代码生成来改善 Bundle 的大小
  • 改善对 Web 平台的兼容性
  • 在没有引入重大更改的 v4 中实施功能时,清除处于怪异状态的内部结构
  • 现在就引入重大更改为将来的功能做准备,使得我们可以尽可能长地使用 v5

迁移指南

请查看此处以获取迁移指南

重大更改:清除

Removed Deprecated Items

所有在 v4 中弃用的选项已被删除。

迁移: 确保你的 webpack 4 版本没有打印出弃用警告。

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

  • IgnorePlugin 和 BannerPlugin 必须只传入一个参数,它可以是对象(Object)、字符串(String)或者函数(function)。

Deprecation codes

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

Syntax deprecated

require.include 已被弃用,当使用它的时候默认会发出警告提示。

可以使用 Rule.parser.requireInclude 将行为改为允许的,但不建议这么使用或者禁用它。

移除自动的 Node.js Polyfills

早期,webpack 这么做是为了运行在浏览器中运行大多数的 Node.js 模块,但是模块的格局发生了变化,现在很多模块的用途是为了前端写的。webpack <= 4 附带了很多 Node.js 核心模块的 ployfills,一旦在项目中使用了 Node.js 核心模块(例如 crypto 模块),这些模块都会自动应用到项目中。

虽然,这使得使用 Node.js 核心模块变得更加简单,但它会将这些巨大的 polyfills 添加到 bundle 中。而在许多情况下,这些 polyfills 是不需要的。

webpack 5 停止自动 polyfilling 这些核心模块,专注于和前端兼容的模块。我们的目标是改善不能使用 Node.js core 核心模块的 web 平台兼容性。

迁移:

  • 尽可能使用与前端兼容的模块。
  • 可以为 Node.js 核心模块手动添加 polyfill。 一些错误消息将会提示如何实现。
  • 软件包作者:使用 package.json 中的 browser 字段使软件包与前端兼容。提供浏览器的替代实现/依赖性。

重大更改:持久缓存

Deterministic Chunk, Module IDs and Export names

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

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

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

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

迁移: 最好使用 chunkIdsmoduleIdsmangleExports 的默认值。你还可以选择使用旧的默认 chunkIds:"size"moduleIds:"size"mangleExports:"size",这将生成较小的 bundles 包,但更经常使它们将无法以进行缓存。

注意:在 webpack 4 中,散列的模块 Ids 会降低 gzip 性能。这与更改的模块顺序有关,并且已得到修复。

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

Real Content Hash

Webpack 5 现在使用 [contenthash] 时将使用文件内容的真实哈希 hash。在此之前,只是使用内部结构的哈希 hash 值。只有当更改注释或重命名变量时,这才会对长期缓存产生积极影响。而在最小化后,这些更改变为不可见。

重大更改:开发支持 Named Chunk IDs(命名的模块 ID)

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

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

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

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

Module Federation

Webpack 5 添加了一个名为 “模块联合” 的新功能,该功能允许多个 Webpack 构建协同工作。从运行时的角度来看,来自多个构建的模块的行为将类似于巨大的连接模块图。从开发人员的角度来看,可以从指定的远程构建中导入模块,并以最小的限制使用它们。

重大更改:新的 Web 平台功能

JSON modules

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

迁移:使用默认导出。

即使在使用默认导出时,optimization.usedExports 优化会删除未使用的属性,并且 optimization.mangleExports 优化处理对应属性。

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

import.meta

  • import.meta.webpackHotmodule.hot 的别名,在严格的 ESM 中也可以使用/
  • import.meta.webpack 是 webpack 主要版本号
  • import.meta.url 是当前文件的 file: url(类似于 __filename,但它是文件地址 url)

Asset modules

Webpack 5 现在可以本地支持 assets 资源模块。这些模块会将文件发送到输出文件夹中,或者将 DataURI 注入到 JavaScript bundler 包中。无论哪种方式,它们都提供了可使用的 URL。

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

  • 匹配此类 import url from "./image.png" 导入时,在 module.rules 设置 type: "asset" (旧的方式)
  • new URL("./ image.png",import.meta.url)(新的方式)

选择了新方式语法,也允许在没有 bundler 下运行代码。此语法在浏览器原生的 ECMAScript modules 中也可用。

Native Worker Support

当将 new URL 获取资源与 new Worker/new SharedWorker/navigator.serviceWorker.register 结合使用时,webpack 将会自动为 web worker 创建一个新的入口。

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

选择该语法是为了允许运行代码也无需 bundler。该语法也适用于浏览器原生的 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 会导致对外部资源的请求(它们是外部资源 externals)

支持请求中的片段:例如:./ file.js#fragment

Async modules

Webpack 5 支持被称为异步的模块 async modules。这些模块不进行同步评估,而是异步 async 和基于 Promise。

通过导入 import 会自动导入它们,并且不需要其他语法,并且其中的差异几乎不会引起注意。

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

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

  • 异步外部 async externals
  • 新规范中的 WebAssembly 模块
  • 使用顶级等待的 Top-level await 的 ECMAScript 模块

Externals

Webpack 5添加了其他的外部类型以涵盖更多应用程序:

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

import: 原生 Native import() 用于加载指定的请求。这个外部模块是异步模块。

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

script:通过 <script> 标记加载地址 url,并从全局变量(及其可选属性)获取导出。这个外部模块是异步模块。

重大更改:Node.js 生态的新功能

Resolving

现在支持 package.json 中的 exportsimports 字段。

Yarn PnP 支持受限。

请在 package export 中查看更多

重大更改:开发体验

Improved target

Webpack 5 允许传递目标 target 列表,并且还支持目标版本。

例如:target:"node14" target:["web","es2020"]

这是向 webpack 提供确认所需所有信息的简单方法:

  • 块 chunk 加载机制
  • 支持的语法,例如箭头函数

Stats

Stats 测试格式在可读性和详细程度方面得到了改进。默认值已得到改进,减少冗长的内容,这也适用于大型版本。

  • 块 chunk 的关系默认情况下是隐藏的。可以使用 stats.chunkRelations 进行切换。
  • Stats 现在可以区分文件 files 和辅助文件 auxiliaryFiles
  • Stats 现在默认情况下隐藏模块 module 和块 chunk ids。可以使用 stats.ids 进行切换。
  • 现在,所有模块的列表均按到入口点的距离排序。可以使用stats.modulesSort进行更改。
  • 块模块 chunk module 列表现在按模块名称排序。可以使用 stats.chunkModulesSort 进行更改。
  • 现在,按拓扑 topologically 对串联模块中的嵌套模块 nested modules 列表进行排序。可以使用stats.nestedModulesSort进行更改。
  • 现在,块 chunk 和资源 asserts 会显示块 chunk id 提示。
  • 资源 assets 和模块 modules 将显示在树中,而不是列表/表中。
  • 一般信息显示在末尾的摘要中。它会显示 webpack 版本,配置名称和警告/错误数。
  • 默认情况下,哈希 hash 是隐藏的。可以使用 stats.hash 进行更改。
  • 默认情况下,不再显示构建时间戳。可以使用 stats.builtAt 启用它。它将在摘要中显示时间戳。
  • 默认情况下,将不再显示子编译。它们可以与 stats.children 一起显示。

Progress

对 CLI 使用 --progressProgressPlugin 进行了一些改进,但也可以手动用作插件。

过去,它仅用于计算已处理的模块。现在,它可以计算 entriesmodules。现在默认显示这些。

过去,它用于显示当前处理的模块。这会导致很多 stderr 输出,并在某些控制台上会产生性能问题。现在,默认情况下禁用此功能(activeModules 选项)。 这也减少了控制台上的垃圾邮件数量。现在,在构建模块期间写入 stderr 的速度限制为 500 ms。

分析模式也得到了升级,将显示嵌套进度消息的计时。 这使得在插件引起性能问题时更容易找出原因。

新添加的 percentBy-option 告诉 ProgressPlugin 如何计算进度百分比。

new webpack.ProgressPlugin({ percentBy: 'entries' });

为了使进度百分比更加准确,ProgressPlugin 会缓存最近的已知模块总数,并在下一次构建中复用此值。 第一个版本将预热缓存,但随后的版本将使用并更新此值。

Automatic unique naming

在 webpack 4 中,多个 webpack 运行时可能在同一 HTML 页面上发生冲突,因为它们使用相同的全局变量进行块加载。为了解决这个问题,需要为 output.jsonpFunction 配置提供自定义名称。

Webpack 5 会自动从 package.json name 推断出构建的唯一名称,并将其用作 output.uniqueName 的默认名称。

此值用于让所有潜在冲突的全局变量唯一。

迁移:删除 output.jsonpFunction,以便在 package.json 中使用唯一名称。

Automatic public path

Webpack 5 将在可能的情况下自动确定 output.publicPath

Typescript typings

Webpack 5 从源代码生成 typescript 类型,并通过 npm 包公开它们。

迁移:删除 @types/webpack。名称不同时更新参考。