Webpack 5 发行版 (2020-10-10)
webpack 4在2018年二月份发行。在那以后我们封装了一些列特性,但是并没有引入破坏性的变化。我们知道大家不喜欢有破环新变化(breaking changes
)的变更。特别是针对webpack这类工具,人们一年只会接触这种应用的机会很有限,其余时候把它丢在一边,只要它能正常工作。但是不引入破换新变化的同时风向新特性是有代价的:我们不能进行核心api或者架构层面的升级。
随着时间的推移,总会到这么一个时间点,面临的问题堆积到了一定程度使得我们必须去通过引入破换性的变化来进行修复,而不是让代码朝着更加不可维护的方向发展。现在是时候发布新版本了,webpack 5包含了架构升级和新特性,我们在实现它们的同时不引入破坏性变更。
与此同时,大版本的发布也是一个修正一些错误、与业界提案和规范保持一致的机会。
所以在今天(202-10-10)webpack 5正式发布,但这并不意味着这个版本已经完成,没有bug,完成了所有的特性开发。与此同时,我们会继续维护webpack 4,修复bug并且增加新特性。接下来的日子里我们主要会进行bug修复,特性的追加会稍后。
常见问题
发行版意味着什么?
这意味着我们完成了破坏性变更的开发。许多顶层的重构已经完成,未来特性(包括当前的特性)的开发打下了很好的基础。
什么时候适合升级?
这个看情况。升级很有可能不会一次成功,你可能需要尝试两次甚至三次。如果你对这个过程并不敏感,那么欢迎你立即升级并且向webpack,插件,loader等提供反馈。我们非常渴望修复其中的问题。它们中的一些已经被修复,你可能就是从他人的反馈中受益的一份子。
赞助更新
webpack完全是靠赞助来维持运转的。它跟一些大公司,比如一些开源项目并无瓜葛。99%的赞助都用来根据贡献的大小支付给webpack代码的贡献者和维护者。我们坚信这样的投资会让webpack发展得更好。
由于疫情的缘故,很多公司对于开源项目的赞助变少了,webpack正处于疫情的不利影响下(正如很多公司和人正经历的那样)。
我们没有能力向贡献者们支付他们本该获得的酬劳,但是现在我们只有一半的资金可用,我们不得不进行开支的削减。直到情况有所好转,我们只会给每月前10天提交代码的贡献者或者维护者支付酬劳。剩下的日子他们都是志愿工作,由他们原本的雇主支付薪水,或者通过做其他的一些事来赚钱。这意味着我们支付的头10天的酬劳将远远大于开发者们付出的时间。
最诚挚的感谢送给trivago
,他们在过去的三年中给webpack提供了大量的赞助。遗憾的是他们无力在今年继续进行赞助,因为他们本身也受到了新冠病毒的影响。我希望其他的公司能够像他们那样慷慨解囊。
感谢所有的赞助者!
总体的方向
这次的发行版改动点集中在以下几个方向:
- 通过持久化缓存提审构建性能
- 通过更好算法和默认设置来提升长期缓存的效果
- 通过web平台来提升兼容性
- 清理掉内部的一些遗留的结构,在v4版本中,为了实现一些特性而引入了一些古怪的状态,这里没有引入破坏性变更
- 为了未来的一些特性,这里使用引用了一些破坏性的变更,这使得我们在v5版本能够停留足够长的时间
迁移指南
详情点击migration guide
主要变更之移除
移除废弃的配置项目
v4中所有的废弃项目被删除。
迁移:确保你的webpack 4打包过程中不会打印任何的有关废弃api的告警。
这里有一些项目被移除了,但是在v4版本中并不会发出废弃api的告警:
- IgnorePlugin和BannerPlugin现在必出传入一个参数,该参数可以是对象,字符串或者函数。
移除代码
新的弃用包括一个弃用代码,因此它们更易于引用。
废弃的语法
require.include
被废弃,使用的时候会有warning。
告警的行为可以在Rule.parser.requireInclude
设置为允许,废弃或禁止。
自动的node.js polyfill被移除
在早些时候,webpack致力于让Node.jsd的模块能够运行在浏览器中,但是情况发生了变化,很多模块仅仅是为了前端的使用而编写。小于等于4版本的webpack为很多Node.js模块封装了polyfills,一旦有某个模块使用了node.js的核心模块这些polifills将会生效(例如crypto
模块)。
这样的兼容使得使用那些为node.js编写的模块非常方便,但是这也导致打包文件中引入了很多的polyfills代码,在很多时候这些polifills都是不必要的。
webpack 5停止自动去兼容这些node核心模块,将开发重心聚焦于前端模块的兼容性。我们的目标是提审web平台的兼容性,很多时候Node.js核心模块都是不必要的。
迁移:
- 尽可能使用具有浏览器兼容性的模块
- 请尽可能手动加入针对Node.js模块的polyfill,将会有报错信息来指导你这样做
- 包作者:请在
package.json
中使用borwser
域,确保包能够在浏览器环境下运行。为浏览器环境提供其他实现或者依赖来解决兼容性问题。
主要变更之长期缓存
确定性的chunk,模块id和导出命名
长期缓存中引入了新的算法,这在生产模式中被自动引入。也就是类似这样的配置:
chunkIds: "deterministic" moduleIds: "deterministic" mangleExports: "deterministic"
算法以确定的方式指定3到5位数字id给模块和chunks,2位字符作为输出的名字。这是在打包体积和长期缓存之间做出的妥协。
moduleIds/chunkIds/mangleExports: false
将会禁止默认行为,你可以通过引入插件来使用常规算法。注意在webpack 4中,设置moduleId/chunkIds:false
却引入插件是可以运行的,但是在wepack 5中必须提供一个插件。
迁移:
最好在chunkIds
,moduleIds
,mangleExports
使用默认值设置。你也可以在配置中使用以前的默认值chunkIds: "size", moduleIds: "size", mangleExports: "size"
.这样打包体积会更小,但是会让打包过程中的缓存更加频繁地失效。(换句话来说,打包速度变慢了,译者注)
注意:在webpack 4中哈希过后的模块id降低了gzip的性能,这跟模块顺序的变更有关,现已被修复。
注意:在webpack 5中,deterministic
id(持久化id)在开发环境下默认启用
真实的内容hash
webpack 5在配置[contenthash]
将会使用文件内容的hash。先前仅仅使用文件内部结构的hash。在仅仅是注释和变量重命名时,将会对长期缓存产生正面影响。这些变化在代码压缩之后是不可见的。
主要变化之开发支持
具名Chunk id
在开发模式中,将会默认启用新的chunk id命名算法,这会给chunk和文件名赋予具有可读性的名字。一个模块(module)的ID取决于它的路径,同上下文有关.一个chunk的ID取决于chunk的内容。
所以你不用再使用类似import(/* webpackChunkName: "name" */ "module")
来debugging,但是如果你想控制生产环境下的文件名时,这样做依然有意义。
在生产环境中很有可能使用chunkIds: "named"
这样的写法,但是请确保这样不会暴露一些敏感的模块名。
迁移:
在开发模式下,如果你不喜欢文件名的随意改变,你可以传入chunkId: "natural"
来使用老的数字命名的形式。
模块联合(module federation)
webpack 5添加了一个新的特性叫做"模块联合",这使得开发者可以使用多个webpack的构建结果来共同工作。从运行时的角度来看,来自不同构建的模块能够被连接在一起成为一个巨大的模块图结构。从开发者的角度来看,模块能能够在最小的限制下从不同的远端构建引入。更对细节请查看这篇单独的指引
主要变动之新的web平台特性
JSON 模块
JSON模块现在遵从协议的规定,如果使用非默认的输出是将会有告警。当从一个严格模式的ECMAScript模块中引入时,JSON模块将不再有具名导出。
迁移:
使用默认导出。
尽管使用了默认导出方式,设置optimization.usedExports
会使得没有使用到的属性被丢弃。设置optimization.mangleExport
绘制的属性名被破坏性优化(译者注:属性名会被修改,减少字符数).
可以通过在rule.parser.parse
中指定JSON的解析器来引入类似JSON结构的文件,比如toml,yaml,json5等等
import.meta
-
import.meta.webpackHot
是module.hot
的别名,这个在严格模式下的ESM中也是生效的 -
import.meta.webpack
是webpack的主版本号 -
import.meta.url
是当前文件的文件url(类似于__filename
,但是作为文件的url)
asset模块
webpack 5对表示assets的模块有原生支持。这些模块要么将一个文件放入输出文件夹,要么将一个DataURI放入javascript打包文件中。这两种方式都提供了一个URL。
可以通过多种方式引用:
- 老方法:
import url from "./image.png"
并且在module.rules
中设置type: "asset"
(当improt的方式匹配的时候) - 新方法:
new URL("./image.png", import.meta.url)
这里的新方法预发允许代码在未打包的情况下运行,这个语法在原生ECMAScript模块中可以在浏览器中执行。
原生worker支持
当new URL
和new Worker / new ShareWorker / navigator.serviceWorker.register
等一起使用的时候,webpack将会为web worker生成一个单独的入口。
new Worker(new URL("./worker.js", import.meta.url))
这个语法也允许在未打包的情况下使用。在原生ECMAScript模块这样使用在浏览器中也是支持的。
URIs
webpack 5支持控制接口请求中的协议。
-
data:
支持。Base64或者原始的编码内容都支持。Mimetype能够被映射到module.rules
中的各个loader。例如:import x from "data:text/javascript,export default 42"
-
file:
支持 -
http(s):
支持。但是需要配置new webpack.experiments.schemesHttp(s)UriPlugin()
***默认情况下目标是web,这些URIs用来获取外部资源(都属于externals)
请求中的分块也是支持的,例如:./file.js#fragment
异步模块
webpack 5支持所谓的异步模块。这些模块并不会同步执行,他们被异步的promise来替代了。
通过import
引入的模块会被自动处理,并不需要额外的操作,这其中的差别几乎无法识别。
通过require()
引入的模块,将会返回一个promise,这个promise的resolve返回原先的exports.
在webpack中,有多个方法引入异步模块:
- 异步externals(async externals)
- 新规范中的WebAssembly 模块
- 使用顶层await 的ECMAScritp模块
Externals
webpack 5中引入了额外的外部文件(external)类型来应对更多的应用场景:
promise:
一个返回Promise的表达式。外部的模块是异步的,并且promise resolve的值就是模块的exports.
import:
原生的import()
是用来加在特定请求的。外部的模块是异步模块。
module:
暂时没有实现,但是打算通过import x from "..."
来实现加载模块的功能
script:
通过