Webpack5 内置缓存方案探索

·  阅读 5119

背景

随着Babel、TypeScript、VueLoader、Terser等编译、转译技术的大规模使用,Webpack的编译时间正不断膨胀。为了优化编译速度,社区主要有两种方案:

  1. 通过把loader的处理结果缓存到本地磁盘,来加速二次编译

  2. 通过预编译dll让webpack跳过一些模块的编译,来加速编译

这些方案在一定程度上解决了编译速度慢的问题,但随之而来的是成堆的配置,严重影响了Webpack的使用体验,甚至出现了“Webpack配置工程师”这种“新职业”。

与此同时,社区出现了一些新兴的编译技术,比如Snowpack,它使用浏览器原生的ES Module实现了O(1)时间的编译,对于“苦Webpack久已”的那些人来说简直不要太有吸引力。

但是这对Webpack来说是一种挑战,面对可能会被蚕食的用户市场,Webpack5的内置缓存方案终于出来了。

长效缓存和编译缓存

长效缓存是浏览器层面的缓存,Webpack通过optimization的splitChunks和runtimeChunk的配置,让编译输出的文件具有稳定的hash名称,从而让浏览器能长期有效、安全的复用缓存,达到加速页面加载的效果。

编译缓存是编译时的缓存,Webpack通过在首次编译后把结果缓存起来,在后续编译时复用缓存,从而达到加速编译的效果。

Webpack5的内置缓存,以及本文所讨论的,都是指编译缓存。

Webpack4的缓存方案

在使用Webpack4以及之前的版本时,我们都会注意到这样的现象:

  • 代码热更新很快

  • npm run star慢

  • npm run build慢

这种现象的本质是:Webpack4在运行时是有缓存的,只不过缓存只存在于内存中。所以,一旦Webpack的运行程序被关闭,这些缓存就丢失了。这就导致我们npm run start/build的时候根本无缓存可用。

所以,解决问题的办法就是把这些Webpack编译过程中的产生的缓存持久化到本地磁盘、数据库或者云端。这里面涉及到两点:要持久化什么持久化到哪里

Webpack本身就已经有一套缓存方案,只是不够完善,不支持持久化。站在现在的角度来看,我们应该直接去完善Webpack核心代码,补充上持久化缓存的功能,使用一套缓存方案解决所有问题,这是显而易见的。

然而,当时社区好像没搞清楚“要持久化什么”这个问题, 他们没有在Webpack核心代码上发力,而是选择了从外部解决。于是就出现了cache-loader、dll等技术,虽然在一定程度上解决了问题,但却引入了过多的复杂性。

Webpack5的缓存方案

实际上,“要持久化什么”这个问题从一开始就是显而易见的:是Webpack运行时存在于内存中的那些缓存,不是loader的产物,更不是dll。因此,Webpack5提供了一套持久化抽象,并提供了几个实现:

  • IdleFileCachePlugin:持久化到本地磁盘

  • MemoryCachePlugin:持久化到内存

根据Webpack运行环境的不同,在dev开发时依旧使用MemoryCachePlugin,而在build时使用IdleFileCachePlugin。

Webpack5直接从内部核心代码的层面,统一了持久化缓存的方案,有效降低了缓存配置的复杂性。除此之外,由于所有被webpack处理的模块都会被缓存,我们npm run start/build的二次编译速度会远超cache-loader,同时dll也可以退出历史舞台了。

Webpack4时之所以要有dll,是因为cache-loader并不能覆盖所有模块,只能对个别被loader处理的模块进行缓存。而那些通用的库是没法被cache-loader处理的,所以只能通过dll的方式来预编译。

实际上,Webpack5的内置缓存方案无论从性能上还是安全性上都要好于cache-loader:

  1. 性能上:由于所以被webpack处理的模块都会被缓存,缓存的覆盖率要高的多

  2. 安全上:由于cache-loader使用了基于mtime的缓存验证机制(参考:juejin.cn/post/684490…),导致在CI环境中缓存经常会失效,但是Webpack5改用了基于文件内容etag的缓存验证机制,解决了这个问题。

具体使用的Webpack5配置官网已经给出了:webpack.js.org/configurati…

FAQ

  1. Webpack4能用上Webpack5的缓存方案吗?

    可以使用hard-source-webpack-plugin,它就是基于上述Webpack5的实现思路的一个插件。但是在#troubleshooting中也可以看到,这里面可能会有一些坑。

  2. Snowpack、Vite这种库可以替代Webpack吗?

    基于Webpack当前庞大的生态,以及Webpack5将要带来的game-changer级别的模块联邦功能,我认为是不能的,但是这种基于浏览器原生ES Module的技术可能会被Webpack采用。


分类:
前端
标签:
收藏成功!
已添加到「」, 点击更改