想把node_modules/.cache目录上传到一个中心化仓库,而后在CI过程构建的时候,能够拉下来重复利用。可是实践中,虽然.cache的确拉下来了,业务代码也一点没变,可是缓存就是没有生效。探究其缘由,最后发现是cache-loader的基于时间(mtime)的缓存验证机制带来的问题。vue
cache-loader做为webpack的loader,会在pitch和loader两个阶段分别作一些事情:node
pitch阶段:校验缓存文件是否可用webpack
loader阶段:判断当前loader的文件是否须要从新生成缓存git
pitch和loader与DOM的capture和pop很像,假设有这样一个loader配置:github
loader: ['cache-loader', 'vue-loader']复制代码
那么pitch阶段的处理流程是:cache-loader -> vue-loader,而loader阶段的处理流程是:vue-loader -> cache-loader。而且,在这两个阶段能够经过一个共享的data对象来传递消息web
根据当前正在处理的文件,读取.cache目录中对应的cache文件,这个文件主要主要有两部份内容:缓存
当前正在处理的文件所依赖的文件bash
当前正在处理的文件,在上一次loader过程当中的产物spa
其中第1点用来判断当前文件的缓存是否依然有效,若是判断有效,那么就直接复用第2点的内容。code
在这个阶段,咱们要判断当前文件是否须要从新生成缓存,判断逻辑很简单:
若是pitch阶段的判断当前文件的缓存失效了,那么loader阶段就要去生成缓存。
在CI环境中,屡次部署之间是隔离的,也就是说node_modules中的全部文件每次都会从新生成,因此node_modules/.cache目录天然就丢失了。
那咱们天然会想到一个办法:在编译后把node_modules/.cache存到云上,而后在下次编译的时候再来下来。的确,这个方案是没问题的,
可是在实施的过程当中,却遇到了一个问题:上述先存再拉的流程确实执行了,可是cache-loader仍是会从新生成缓存,并无利用上。
基于这个现象去看了cache-loader代码后发现,在cache-loader的pitch阶段,它的“判断当前文件的缓存是否依然有效”的方法是:基于文件最后修改时间(mtime)来判断。
简单的来讲就是:咱们虽然从云上拉下来了上一次编译产生的缓存文件,与yarn从新安装的node_modules里的文件,存在mtime不一致问题,致使判断为“缓存失效”。
找到问题的缘由后,翻了翻cache-loader的issues,发现你们也在吐槽只支持mtime判断这件事,可是cache-loader不打算推出新的判断方式,而是推荐你们等webpack5的多级缓存功能。
基于这个事实,咱们只能去二次开发cache-loader,让它支持新的对比方式,从而解决上述问题。
cache-loader提供了自定义compare方法的接口,可是这个接口回调的参数没法让咱们去获取文件hash,因此至关因而个摆设。
同时这个compare接口也已经上线了,再去改compare接口的回调参数,是一个breaking change,也不太现实。
咱们天然想到判断文件是否发生变更的方法:hash对比。由于hash不会随时间地点变化,它能够完美解决上述问题。具体方案是:
在pitch阶段使用hash对比判断缓存是否有效,若是缓存无效或没有缓存,接着在loader阶段读取文件并生成hash存入到缓存中。