浏览器的缓存机制

分为:强缓存和协商缓存

强制缓存优先于协商缓存进行,若强制缓存(Expires和Cache-Control)生效则直接使用缓存,若不生效则进行协商缓存(Last-Modified / If-Modified-Since和Etag / If-None-Match),协商缓存由服务器决定是否使用缓存,若协商缓存失效,那么代表该请求的缓存失效,返回200,重新返回资源和缓存标识,再存入浏览器缓存中;生效则返回304,继续使用缓存

一、强缓存的实现:

HTTP Header 中设置:Expires 和 Cache-Control,通过这两个字段,浏览器会来判断目标资源是否命中强缓存,

命中强缓存

没有命中强缓存:

Expires

缓存过期时间,需要和Last-modified(服务器端最后更新的时间)结合使用。Expires是Web服务器响应消息头字段,在响应http请求时告诉浏览器在过期时间前浏览器可以直接从浏览器缓存取数据,而无需再次请求

因为基于本地时间的Expires的字段太过依赖于“本地时间”,如果服务端和客户端的时间设置可能不同,或者用户直接手动去把客户端的时间改掉,那么 Expires 将无法达到我们的预期。

所以我们引入Cache-Control

Cache-Control

HTTP1.1中新增了Cache-Control字段

常用指令:

  • public:所有内容都被缓存,浏览器和代理服务器都可以缓存
  • private(cache-control的默认值):只有浏览器可以缓存,代理服务器不可以

publicprivate 是针对资源是否能够被代理服务缓存而存在的一组对立概念

public:如 Browser <– proxy1 <– proxy2 <– Server,中间的proxy可以缓存资源,比如下次再请求同一资源proxy1直接把自己缓存的东西给 Browser 而不再向proxy2要

private:对于Browser <– proxy1 <– proxy2 <– Server,proxy 会老老实实把Server 返回的数据发送给proxy1,自己不缓存任何数据。当下次Browser再次请求时proxy会做好请求转发而不是自作主张给自己缓存的数据

  • no-store:拒绝浏览器缓存,包括服务端的缓存也不允许
  • no-cache:每一次发请求都不使用浏览器缓存,直接去服务端确认数据是否过期(也就是后面说到的协商缓存)
  • max-age:max-age=xxx (xxx is numeric)表示缓存内容将在xxx秒后失效
  • s-maxage:s-maxage 优先级高于 max-age,两者同时出现时,优先考虑 s-maxage。如果 s-maxage 未过期,则向代理服务器请求其缓存内容

s-maxage也是只要针对大型代理架构中,不得不考虑代理服务器的缓存问题,仅在代理服务器中生效

二、协商缓存的实现:

协商缓存依赖于服务端与浏览器之间的通信,浏览器与服务器合作之下的缓存策略。

协商缓存机制下,浏览器需要向服务器去询问缓存的相关信息,进而判断是重新发起请求、下载完整的响应,还是从本地获取缓存的资源。

协商缓存生效,返回304和Not Modified

协商缓存失效,返回200和请求结果

image-20201208101512021

Last-Modified

  1. Last-Modified 是一个时间戳,如果我们启用了协商缓存,它会在首次请求时随着 Response Headers 返回:

  1. 浏览器下一次请求这个资源,浏览器检测到有 Last-Modified这个header,于是添加If-Modified-Since这个header,值就是Last-Modified中的值;服务器再次收到这个资源请求,会根据 If-Modified-Since 中的值与服务器中这个资源的最后修改时间对比,如果没有变化,返回304和空的响应体,直接从缓存读取,如果If-Modified-Since的时间小于服务器中这个资源的最后修改时间,说明文件有更新,于是返回新的资源文件和200

Etag

  1. Etag是服务器响应请求时,返回当前资源文件的一个唯一标识(由服务器生成),只要资源有变化,Etag就会重新生成

  1. 浏览器在下一次加载资源向服务器发送请求时,会将上一次返回的Etag值放到request header里的If-None-Match里,服务器只需要比较客户端传来的If-None-Match跟自己服务器上该资源的ETag是否一致,就能很好地判断资源相对客户端而言是否被修改过了。如果服务器发现ETag匹配不上,那么直接以常规GET 200回包形式将新的资源(当然也包括了新的ETag)发给客户端;如果ETag是一致的,则直接返回304知会客户端直接使用本地缓存即可

两者之间对比:

  • 首先在精确度上,Etag要优于Last-Modified。

Last-Modified的时间单位是秒,如果某个文件在1秒内改变了多次,那么他们的Last-Modified其实并没有体现出来修改,但是Etag每次都会改变确保了精度;如果是负载均衡的服务器,各个服务器生成的Last-Modified也有可能不一致。

  • 第二在性能上,Etag要逊于Last-Modified,毕竟Last-Modified只需要记录时间,而Etag需要服务器通过算法来计算出一个hash值。
  • 第三在优先级上,服务器校验优先考虑Etag

缓存位置

  • Service Worker Cache
  • Memory Cache
  • Disk Cache
  • Push Cache

Memory Cache

  • 内存中的缓存,也就是占用内存,所以容量较小

  • 浏览器会最先去尝试命中该缓存

  • 响应速度最快

  • 时效短,浏览器的tab页关闭,内存中的缓存也就被释放

Disk Cache

  • 硬盘中的缓存
  • 时效长,除非你手动清除
  • 相比memory cache容量更大

Service Worker Cache

  • 基于https协议

Service Worker 是一种独立于主线程之外的 Javascript 线程。它脱离于浏览器窗体,因此无法直接访问 DOM。这样独立的个性使得 Service Worker 的“个人行为”无法干扰页面的性能,这个“幕后工作者”可以帮我们实现离线缓存、消息推送和网络代理等功能。我们借助 Service worker 实现的离线缓存就称为 Service Worker Cache

Push Cache

  • 基于HTTP2
  • 缓存的最后一道防线
  • 只存在session中,会话结束缓存便被释放
  • Push Cache 中的缓存只能被使用一次