浏览器资源缓存策略
浏览器的缓存大致分为四个方面
- Memory Cache(disk Cache)
- Service Worker Cache
- HTTP Cache
- Push Cache
HTTP Cache
- 浏览器在请求资源前会优先检测本地是否有命中强缓存。
- 强缓存分expires与Cache-Control两种。
- 如果命中强缓存则会向 memory cache或disk cache等多种缓存中去获取。
- 如果没有命中则会向服务端去请求资源。
所以优先顺序是强缓存--->协商缓存--->源服务器获取
HTTP缓存分为强缓存与协商缓存强缓存使用expires与Cache-Control等属性来控制
强缓存
expires
expires是HTTP/1.0时期提出的,主要是由服务端设置过期时间,然后浏览器通过对比本地时间与expires来确定资源是否过期是不是需要向服务端去索取资源。
expires: Wed, 11 Sep 2019 16:12:18 GMT
缺点: expires是通过浏览器本地时间来对比的,如果人为的修改本地时间会导致资源缓存强缓存命中失败,重新去获取资源
Cache-Control
Cache-Control是HTTP/1.1作为expires全面的代替者提出的,通过对Cache-Control设置不同属性来进行资源缓存的判定
其属性值有
- no-cache: 数据内容不能被浏览器缓存, 每次请求都重新访问服务器确认是否过期(进行协商缓存), 若有max-age, 则缓存期间不访问服务器.
- no-store: 浏览器、服务器、代理服务器等全部不能缓存,
- private(默认): 只能在浏览器中缓存, 只有在第一次请求的时候才访问服务器, 若有max-age, 则缓存期间不访问服务器.
- public: 可以被任何缓存区缓存, 如: 浏览器、服务器、代理服务器等
- max-age: 相对过期时间, 即以秒为单位的缓存时间.
- s-max-age: 与max-age相似,两者同时存在时优先使用s-max-age,并只在代理服务器使用且只对public有效
- no-cache, private: 打开新窗口时候重新访问服务器 , 若设置max-age, 则缓存期间不访问服务器.
- private, 正数的max-age: 后退时候不会访问服务器
- no-cache, 正数的max-age: 后退时会访问服务器
cache-control: max-age=3600, s-maxage=31536000
如果只设置了max-age,默认会采取public模式模式可以被全缓存的
协商缓存
协商缓存是浏览器与服务器之间的通讯,浏览器向服务器询问相关信息,确定本地文件是否过期是否需要向服务器重新获取资源,如果资源没有变动则会重定向至浏览器端并且此时的Status Code为304 Not Modified
那么协商缓存缓存是怎么实现的呢?
- 通过Last-Modified与ETag来进行
Last-Modified
当cache-control为no-cahce时在响应头上会带上Last-Modified与ETag,同时Last-Modified是一个时间戳。
- 如果修改了文件但是没有修改内容,这样文件的修改时间同样会发生变化,同样触发校验时则会导致触发一次资源的重新请求
- 因为Last-Modified与If-Modified-Since都是使用时间戳来设置的,只要修改保存的时间足够快在100ms内完成了改动,则时间戳并不会变化,使得在校验时无法检验出变化,没有对新资源进行请求。
为了解决这样的问题,ETag也作为Last-Modified的加强与补充出现了
ETag
ETag是服务器给资源生成的标识字符串,作为Last-Modified的加强与补充,他跟Last-Modified工作原理很相似,在资源的响应头中生成ETag,在后续请求的请求头中会生成If-None-Match,通过比对两者的差异,如果Last-Modified与ETag同时存在时,对ETag进行优先判定
(对ETag与Last-Modified的补充)
Nginx下配置Last-Modified时(ETag是可配置的) Apache默认是两者都会返回
那么问题来了ETag作为Last-Modified的强化与补充如果只有ETag是否也会触发交互?
结果是:ETag可以单独使用,与服务端进行资源判定
HTTP缓存指南
那对于这些资源设置应该是怎么样的呢?我们这里参考一下Chrome给出的流程图
no-store,如果需要复用则将cache-control设置为no-cache,随后我们根据资源是不是需要被代理服务器缓存来设置public或者private,最后再设置max-age;最后在设置Last-Modified与ETag等属性。
MemoryCache与DiskCache
MemoryCache:在服务器内存空余的时候优先使用Memory Cache随后才会考虑使用disk Cache,
因为Memory Cache读取速度是最快的同时也是最短命的在浏览器关闭该页面时就会销毁资源,disk Cache读取速度比Memory Cache要慢但是由于它是存在硬盘中的所以它的存在时间是最长的也是最稳定的。
Service Worker Cache
上面介绍了httpcache又介绍了memoryCache,现在来介绍一下一个更陌生的service worker cache。
我们书写的js代码通常是在主线程运行,可以访问DOM与Windows全局变量,Service Worker与Web Worker则是独立于主线程的JavaScript线层,因为他被设计成完全异步的,所以她不会阻塞页面渲染也不会阻塞JavaScript主流程的执行所以我们可以用它去缓存离线资源,推送消息等。
同时Service Worker对协议也是有要求的,必须是https,但是这个对于我们本地调试或者开发其实是不友好的,不过还算Service Worker还算是人性化,可以再localhost跟127.0.0.1环境下运行,同时github也可以执行相关的代码。
这里我们准备一个demo
ServiceWorker registration successful with scope: http://127.0.0.1:8080/