-
Notifications
You must be signed in to change notification settings - Fork 6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
基于webpack4[.3+]构建可预测的持久化缓存方案 #49
Comments
文章写的很赞。 |
感谢指正。 |
楼主,你的demo还在吗,我在异步加载时配置了NamedChunksPlugin还是全部生成了一遍,想看下你的demo |
这里应该为 moduleIds: 'hashed',最靠谱的官方文档 |
楼主,看了您的文章受益匪浅,非常感谢。 |
在webpack4.3时,异步模块是没有chunk Name的,所以会使用数字自增。 |
您好,我尝试下异步模块,确实会有这个问题,必须需要加入NamedChunksPlugin去解决,但是我现在的困惑更多了,NamedChunksPlugin和HashedModuleIdsPlugin有什么区别,为什么文档上写着只采用一个即可,这里说的不同mode下开启不同plugin,这些plugin没找到文档呢? |
webpack文档一直不算太好,而且内部机制过于复杂,个别稍微小众的plugin就需要去专门的github看issue和源码了。 |
好的,谢谢~ |
基于webpack4[.3+]构建可预测的持久化缓存方案
web缓存的好处不用多说,自从webpack一桶江湖后,如何做Predictable long term caching with Webpack让配置工程师们头疼不已。
webpack4.3前,有相当多的文章介绍如何处理(见参考),这里想做些更到位的探索。
问题
当业务开发完成,准备上线时,问题就来了🤡:
不要放弃治疗🍷本文测试时候的一些版本:
TL;DR
contenthash
很爽很安逸🌈HashedModuleIdsPlugin
稳定moduleId。该插件会根据模块的相对路径生成一个四位数的hash作为模块id, 建议用于生产环境🎁NamedModuleIdsPlugin
稳定chunkId。需要长效缓存的资源
图片、字体等media资源
media资源可以使用
file-loader
根据资源内容生成hash值,配合url-loader
可以按需内联成base64格式,这里不多说。css
css资源如果不做特殊处理,会直接打进js文件中;生产环境我们通常会使用
mini-css-extract-plugin
抽取到单独的文件中或是内联。js
js文件的处理要麻烦的多,作为唯一的入口资源,js管理着其他module,引入了无穷无尽的疑问,这也是我们接下来的重点。
webpack4 hash类型
contenthash应该是一个比较重要的feature,webpack核心开发者认为这个可以完全替代chunkhash(见 issue#2096),也许会在webpack5中将contenthash改成
[hash]
。那么他们的区别在哪里呢?
简单来说,当chunk中包含css、wasm时,如果css有改动,chunkhash也会发生改变,导致chunk的哈希值变动;如果使用contenthash,css的改动不会影响chunk的哈希值,因为它是依据chunk 的js内容生成的。
知道有这么几种就够了,下面就从最基本的例子开始吧🚴♂️。
栗子们
接下来都会在
production mode
下测试(如果你不清楚webpack4新增的mode模式,去翻翻webpack mode 文档吧)。涉及到的拆包策略,会一笔带过,后续有时间再详细聊聊拆包相关的问题~
1. 简单的hash
最简单的配置文件如下👇,
入口文件
index.js
很简单:打包结果:
这个例子使用了
name + hash
进行文件命名,因为hash是根据module identifier
生成的,这意味着只要业务中有一点点小小的改动,hash值就会变,来看下面的例子。2. 增加一个vendors
让我们来增加一点点复杂性。
@灰大 在对Webpack的hash稳定性的初步探索中展示了一个有趣的例子,我们也来试试看。
现在我们给入口文件增加一个a.js模块:
a模块引入了lodash中的identity方法:
然后修改下webpack配置文件,以便抽出vendors文件及manifest。这里多说一句,runtimeChunk非常的小,同时可预见的并不会有体积上的大变,所以可以考虑内联进html。
打包结果是:
[hash] 的问题
相信你已经注意到了,上图打包后,所有的文件都具有相同的hash值,这意味着什么呢?
每一次业务迭代上线,用户端要重新接收静态资源,因为hash值每次都会变动,之前的一切缓存都失效了😬。
所以,我们想要做持久化缓存,肯定是不会用
[hash]
了。3. chunkhash了解一下?
在webpack4.3之前,我们只能选择chunkhash进行模块标识,然而这个玩意儿如不是很稳,配置工程师们废了九牛二虎之力用了各种黑科技才将hash值尽可能的稳定。
新出的contenthash和chunkhash有多大的区别呢😳?
来看下面几个例子。
使用chunkhash
我们将
[hash]
换成[chunkhash]
,看下打包结果:index、vendors和runtime都拥有了不同的哈希值,so far so good。
我们继续灰大的例子,在index.js中增加b.js模块,b模块只有一行代码:
打包结果:
index文件的哈希值变动符合预期,但是vendors的实质内容仍然是lodash包的identity方法,这个也变了就不能忍了。
原因是webpack4默认按照resolving order使用自增id进行模块标识,所以插入了b.js导致vendors的id错后了一个数,这一点我们diff一下两个vendors文件就可以看出,两个文件只有这里不同:
灰大文章中也提到了,解决方案很简单,使用
HashedModuleIdsPlugin
,这是一个内置插件,它会根据模块路径生成模块id,问题就迎刃而解了:(起初比较担心根据module path进行hash计算后命名,这样的方式是否会因操作系统不同而产生差异,毕竟已经吃过一次亏了,见windows/linux下path路径不一致的问题 ,好在webpack官方已经处理过这个问题了,无需操心了)
(设置
optimization.moduleIds:'hash'
可以达到同样的效果,不过需要[email protected]以上)打包结果:
4. 增加一个css 模块
入口文件增加c.css👇,c的内容不重要:
配置一下
mini-css-extract-plugin
将这个css模块抽取出来:然后打包。
改动一点c.css中的内容,再次打包。
这两次打包过程,我们只对c.css文件做了改动,预期是什么呢?
当然是希望只有css文件的哈希值有改动,然而事情并不符合预期:
注意看index.js的哈希值📌
打包后,入口文件的哈希值竟然也变了,这就很让人头疼了。
5. contenthash治愈一切?
contenthash并不能解决moduleId自增的问题
使用contenthash和chunkhash,在上述vendors文件的行为上,有什么样的区别呢?
能否解决因模块变动的问题?
答案是不能😅。
毕竟文件内容中包含了变动的东西,还是需要
HashedModuleIdsPlugin
插件。contenthash威力所在
contenthash可以解决的是,css模块修改后,js哈希值变动的问题。
修改配置文件👇:
直接来看对比:
可以看到,index.js的chunk 哈希值在改动前后是完全一致的💯。
6. 增加异步模块
为了优化首屏性能或是业务变得原来越臃肿时,我们不可避免的会进行一些异步模块的抽取和加载,通过dynamic import方式就很安逸。
然而,异步模块作为一个新的chunk,他的哈希值是啥样的嘞?
我们增加一个异步模块试试看。
async-module的内容也是不重要,重要的是增加这个模块前后的哈希值有了很大的变化!
没有异步模块:
增加异步模块:
再增加第二个异步模块:
上面的对比简直是一夜回到解放前。。。除了css文件的哈希值在线,其他的都发生了改变。
究其原因,是因为虽然我们稳定住了moduleId,但是对chunkId无能为力,而且异步的模块因为没有chunk.name,导致又使用了数字自增进行命名。
好在我们还有
NamedChunksPlugin
可以进行chunkId的稳定👇:除此之外还有其他的方式可以稳定chunkId,不过由于或多或少的缺点在这里就不赘述了,来看现在打包的结果:
可以看出,异步模块也都有了name值,同时vendors的哈希值也回归了。
7. 增加第二个入口文件
在业务迭代过程中,经常会增删一些页面,那么这样的场景,哈希值是如何变化的呢?
我们增加一个index2入口文件,内容是一句
console.log('i am index2~')
,来看打包结果:可以看到,除了增加了index2.js和runtime~index2.js这两个文件外,其余文件的哈希值都没有变动,完美😉
原因是我们已经稳定住了ChunkId,各个chunks不会再根据resolving order进行数字自增操作了。
在实际生产环境中,当新引入的chunk依赖了其他公用模块时,还是会导致一些文件的哈希值变动,不过这个可以通过拆包策略来解决,这里就不赘述了。
总结
本文通过一些例子,总结了通过webpack4做长效缓存的原理以及踩坑实践,而且这些已经运用在了我们的实际业务中,对于频繁迭代的业务来说,有相当大的性能提升。
webpack4的长效缓存相比之前的版本有了很大的进步,也有些许不足,但是相信这些在webapck5中都会得到解决🙆♀️~
参考
The text was updated successfully, but these errors were encountered: