强制缓存下更新文件
导读
对于页面加载所需的资源,通常会利用浏览器自身的缓存策略避免重复加载,但每当版本迭代,总希望用户能访问最新的功能对应的文件资源,而不是缓存中的,特别是对于强制缓存而言,即使设置了Cache-Control:max-age=3156000
,也会希望更新文件,以下就是可行的方案,共实际选择。
使用文件指纹
笔者之前有过实践,在一个vue2的SPA项目中,可以通过文件指纹的方式,利用webpack
的contenthash
实现。
总体原理是旧版本的文件名是app.8934.js
,已经被浏览器缓存,而其能被加载是因为index.html
文件引入,而新版本app.9876.js
由于没被引入没被缓存过,只要不缓存index.html
,每次自然会引入新版本资源而不是旧版本资源。
1 | module.exports = { |
1 | server { |
除了webpack
的配置,还需nginx
的支持。
1 | server { |
对于vite,可以使用rollup-plugin-hash
实现。
1 | import { rollup } from 'rollup'; |
实现一个文件指纹,还可以使用fingerprintjs。
使用查询参数(Query String)
例如,可以在具体文件后增加查询参数
1 | <script src="app.js?v=1.0.1"></script> |
对比上面的文件指纹,其不需要构建工具的支持,但可能存在代理服务器不支持导致失效的问题。
使用 Cache-Control: no-cache
或 must-revalidate
可以通过设置cache-control
请求头,每次都向服务器发送请求。
1 | Cache-Control: no-store |
或者
1 | Cache-Control: max-age=31536000,must-revalidate |
但是这样每次都会向浏览器发送请求,将导致无法充分利用浏览器的缓存能力。
使用Service Worker
利用Service worker
能拦截网络请求的功能,可以更为精确地控制缓存,实现更为灵活的缓存控制机制。
- 注册
Service Worker
1 | if ('serviceWorker' in navigator) { |
- service worker 文件
1 | self.addEventListener('install', (event) => { |
此措施可以支持离线访问,但需要额外的配置,还需要注意不同浏览器的兼容问题。
使用版本化路径
使用版本化路径,应对既要新版功能,又要保留原有版本功能的场景。
1 | <script src="/v1.0.1/app.js"></script> |
但是需要Nginx等部署工具的支持,缺点是需要手动更新。
但其实也是不得缓存index.html
文件,或者只缓存很短的时间。
总结
总而言之,强制缓存下更新文件可以通过文件指纹、查询参数、service worker和版本化路径解决,其中文件指纹和版本化目录都需要部署工具的配合(Nginx),而service worker可以实现离线缓存,版本化可以保留原有的功能等。