只有不断的学习,才会发现自己是多么无知。

不过如果站在巨人的肩膀上的话,我就比别人看的更高更远。今天就来谈谈HTTP缓存机制。

缓存是啥?

在浏览一些网站的时候,你有没有发现第二次浏览的时候比一开始的快很多。其实这就是缓存的结果,把第一次加载的网络资源缓存下来,以后请求的时候不用再去服务器获取,直接在本地获取即可。
这样可以加快资源获取速度,提升用户体验,减少网络传输,缓解服务端的压力,可谓是一石多鸟啊。

HTTP 缓存分为 2 种,一种是强缓存,另一种是协商缓存。我们来看一个缓存的流程图

每次浏览器发起GET请求(POST请求不能缓存)都会判断是否有缓存,首先判断的是强缓存,有则返回资源给浏览器渲染;否则判断是否有协商缓存,有返回资源给浏览器渲染,否则向服务器请求资源。

强缓存

当有强缓存在时不需要发送请求到服务端,直接读取浏览器本地缓存,在 Chrome 的 Network 中显示的 HTTP 状态码是 200 。
强缓存又分为 Disk Cache (存放在硬盘中)和 Memory Cache (存放在内存中),存放的位置是由浏览器控制的。是否强缓存由 ExpiresCache-ControlPragma 3 个 Header 属性共同来控制。
优先级:Pragma > Cache-Control > Expires;

Expires

Expires 的值是一个 HTTP 日期,在浏览器发起请求时,会根据系统时间和 Expires 的值进行比较,如果系统时间超过了 Expires 的值,缓存失效。
Expires有一个很大的弊端,就是它返回的是服务器的时间,但判断的时候用的却是客户端的时间,这就导致Expires很被动,因为用户有可能改变客户端的时间,导致缓存时间判断出错,Expires是HTTP1.0的产物,现在大多数使用Cache-Control替代。

Cache-Control

Cache-Control 是 HTTP/1.1 中新增的属性,在请求头和响应头中都可以使用,常用的属性值如有:

  • max-age:单位是秒,缓存时间计算的方式是距离发起的时间的秒数,超过间隔的秒数缓存失效
  • no-cache:不使用强缓存,需要与服务器验证缓存是否可用
  • no-store:禁止使用缓存(包括协商缓存),每次都向服务器请求最新的资源
  • private:专用于个人的缓存,中间代理、CDN 等不能缓存此响应
  • public:响应可以被中间代理、CDN 等缓存
  • must-revalidate:在缓存过期前可以使用,过期后必须向服务器验证
    1
    Cache-Control: max-age=100, must-revalidate, public
    上面指令的意思是缓存的有效时间为100秒,之后访问需要向源服务器发送请求验证,此缓存可被代理服务器和客户端缓存。
Pragma

Pragma 只有一个属性值,就是 no-cache ,效果和 Cache-Control 中的 no-cache 一致,不使用强缓存,需要与服务器验证缓存是否可用;
当 Pragma 和 Cache-Control 同时存在的时候,Pragma 的优先级高于 Cache-Control。
Pragma属于通用首部字段,在客户端上使用时,常规要求我们往html上加上上面这段meta元标签。

1
<meta http-equiv="Pragma" content="no-cache">

协商缓存

当浏览器的强缓存失效的时候或者请求头中设置了不走强缓存,并且在请求头中设置了If-Modified-Since 或者 If-None-Match 的时候,会将这两个属性值到服务端去验证是否命中协商缓存,如果命中了协商缓存,会返回 304 状态,加载浏览器缓存,并且响应头会设置 Last-Modified 或者 ETag 属性。

Last-Modified/If-Modified-Since

在浏览器第一次请求某一个URL时,服务器端的返回状态码会是200,响应的实体内容是客户端请求的资源,同时有一个Last-Modified的属性标记此文件在服务器端最后被修改的时间。

1
Last-Modified : Fri , 11 May 2019 18:53:33 GMT

当浏览器第二次请求这个URL的时候,根据HTTP协议规定,浏览器会把第一次Last-Modified的值存储在If-Modified-Since里面发送给服务端来验证资源有没有修改。

1
If-Modified-Since : Fri , 11 May 2019 18:53:33 GMT

服务端通过If-Modified-Since字段来判断在这两次访问期间资源有没有被修改过,从而决定是否返回完整的资源。如果有修改正常返回资源,状态码200,如果没有修改只返回响应头,状态码304,告知浏览器资源的本地缓存还可用。

ETag/If-None-Match

代表的是一个资源的标识符,当服务端的文件变化的时候,它的 hash码会随之改变,通过请求头中的 If-None-Match 和当前文件的 hash 值进行比较,如果相等则表示命中协商缓存。ETag 又有强弱校验之分,如果 hash 码是以 “W/“ 开头的一串字符串,说明此时协商缓存的校验是弱校验的,只有服务器上的文件差异(根据 ETag 计算方式来决定)达到能够触发 hash 值后缀变化的时候,才会真正地请求资源,否则返回 304 并加载浏览器缓存。
If-None-Match和If-Modified-Since同时存在的时候If-None-Match优先级更高
ETag/If-None-Match 的出现主要解决了 Last-Modified/If-Modified-Since 所解决不了的问题:
如果文件的修改频率在秒级以下,Last-Modified/If-Modified-Since 会错误地返回 304
如果文件被修改了,但是内容没有任何变化的时候,Last-Modified/If-Modified-Since 会错误地返回 304
好了,HTTP缓存就说到这了,虽然还有很多字段没说,比如 VaryDateAge