强缓存(本地缓存)
浏览器在第一次请求资源后,再次请求该资源时,会先获取该资源缓存的header信息,如果命中强缓存,则不继续请求服务端数据,直接使用本地缓存数据,不会与服务器通信、
相关字段
http1.0中使用expirse,用于指定有效期截止时间,如果发送请求的时间在expirse之前就会使用本地缓存
http1.1中使用Cache-Control:max-age=num,num是一个相对值,再次发送请求时会使用资源第一次请求的时间与num相加计算出资源过期时间,再与当前请求时间比较,在过期时间之前则使用本地缓存
协商缓存
由服务器决定是否使用缓存数据,服务器根据相关header字段判定
Last-Modified/If-Modified-Since
(1)第一次请求资源,服务器的response header中会携带 Last-modified标示文件修改时间,
(2)再次请求该资源时request header会使用这个(1)Last-Modified值作为If-Modified-Since的值
(3)服务器在收到If-Modified-Since后会与资源在服务器上的最后修改时间做对比,对比一致则返回304通知浏览器使用本地缓存。
(4)如果服务端没有命中缓存,则正常返回数据,并在reponse header中携带Last-Modified
Etag/If-None-Match
判断过程与Last-Modified类似,不同的是服务端返回304时也会重新生成Etag并携带在reponse header中即使这个Etag没有变化
参考
Last-Modified与Etag
Last-Modified与Etag各有优缺点,根据实际情况选择
Etag解决的问题:
- 有些周期性修改的文件,只有修改时间改变,文件内容并没有修改,这时我们希望使用的是缓存数据,Last-Modified不能达到需求
- Last-Modified时间有精度是s级,如果文件在1s内有修改,这是Last-Modified的判断就会失效
- 有写服务器不能精确计算文件的修改时间
Last-Modified解决的问题:
- 有写文件生成Etag的代价较大,对性能有影响
Cache-Control
no-store:不使用浏览器缓存
no-catch: 使用协商缓存
max-age: 强缓存
public: 可以被CDN等中间代理缓存
private:仅客户端可缓存
最佳缓存策略
项目中遇到过的问题
强缓存在的问题
项目中存在一个名为pluginA.js的静态文件,pluginA是一个上传的插件
问题:部分用户无法使用上传功能
解决过程:
- 首先排查是不是自己使用API的姿势有问题,尝试过后没有发现可疑的点
- 有可能是插件本身存在的bug,查看插件issue未找到相关问题,尝试解读源码,并在源码中可能存在问题的地方加入自己的测试代码
- 测试时发现测试代码没有生效,进而发现,有问题的用户的pluginA.js是更新之前的版本(pluginA.js曾经修改过)
- 查看http请求头,Cache-Control的过期时间设置为一年,而PluginA.js文件一直使用的是6个月前缓存的版本
- 考虑到用户并不是计算机相关工作者,6个月不清缓存也是有可能发生的,建议后端将强缓存调整为协商缓存
service worker
问题:一个使用vue-cli搭建的项目,每次上线新功能都需要用户清除缓存才能使用
解决过程:
-
首先考虑HTTP缓存了index.html,因为其他js\css等静态文件都是有hash的,不存在缓存的情况
-
将HTTP缓存方式修改为协商缓存后,index.html仍然需要清空缓存才能更新
-
google相关资料,发现浏览器在使用缓存数据时,优先级最高的是service worker中缓存的文件,然后才是HTTP缓存
-
service worker是由于在项目搭建时开启了PWA,service worker缓存可以在Chrome的开发者工具–>Application–>Cache Storage中查看
-
service worker中的文件缓存时永久性的,一旦缓存只有两种情况可以清除:1、容量超出限制,浏览器自动清除;2、开发者通过调用API清除
-
首先,通过webpack配置关闭service worker(代码如下),但关闭之后仍不能清除service worker中已经缓存的文件
pwa: { serviceWorker: false, },
-
想办法绕开service worker的缓存,使用HTTP缓存,没有找到相关方法,似乎是不可能的,乖乖使用API清除原有缓存
caches.keys().then(function(names) { for (let name of names) caches.delete(name) })
vue-cli搭建的项目,在启用PWA后会在src目录下生成registerServiceWorker.js将上面的代码加入registered钩子函数中即可
清空缓存并硬性重新加载可以更新文件------
似乎说明清空缓存会更新service worker缓存的内容
待确认:service worker的数据存储是怎么划分的?