浏览器缓存机制

一、缓存类型

  1、缓存类型分为强缓存和协商缓存

  • 强缓存:在用户请求资源时,如果命中强缓存,则不向服务器请求,而直接从本地获取资源。我们可以看到200状态码,并提示from disk cache或from memory cache
  • 协商缓存: 在用户请求资源时,浏览器直接则向服务器发送请求,服务器根据 request header 来判断是否命中协商缓存,如果命中,则返回304和新的response header,使用本地资源;否则,返回新的资源。


2、区别

  • 共同点:两者命中后都是从本地读取资源。
  • 不同点: 强缓存很强势,是没有向服务器发出请求的; 而协商缓存必须要向服务器发一个请求来协商。


二、强缓存header

强缓存是利用http的响应头中的Expires字段和Cache-Control两个字段来控制,用来表示使用缓存的有效时间。

1、Expires

  Expires是http1.0规范的,表示缓存的过期时间。 如某个资源的response heade中的字段: Expires: Fri, 18 Aug 2017 07:57:17 GMT。 表示当浏览器再次加载这个资源时,如果时间没有超过,就命中强缓存,使用内存中缓存的资源。

之所以浏览器在再次加载时可以判断出时间是否超过,是因为浏览器在缓存资源时,不仅缓存了资源,还缓存了response header相关的内容,比如这里Expires字段。

缺点:由于不能保证服务器和用户端的绝对时间保持一致,所以缓存有时可能会出现混乱的情况, 在HTTP1.1版本中开始使用Cache-Control的方法进行缓存。


2、Cache-Cntrol

  Cache-Control是http1.1规范的,同样表示缓存的过期时间。 其中的max-age是作为判断是否过期的主要判据,它是一个相对时间,单位为s。 如知乎上的某一张图片的response header中的字段:cache-control: public, max-age=31536000。 public代表了这张图片是可以被任何用户缓存的,包括代理服务器等; 而max-age是表示在31536000s(一年)内,如果再次请求就使用本地资源。Cache-Control除了max-age的使用之外,还有几个比较重要的字段:

  • no-cache: 不优先使用本地缓存,而是使用协商缓存。注意: 这里并不是说一定不适用本地缓存的资源,而是需要先协商一下,如果命中,还是会使用本地缓存的。
  • no-store:一定不使用本地缓存,每次用户请求资源,都会下载得到服务器发来的最新的资源。
  • public:资源可以被任何用户缓存,包括所有普通用户和代理服务器。
  • private:只能被当前的特定用户缓存,其他用户无法缓存。 一般是说代理服务器不能缓存。


  

3、Expires、Cache-Control比较

相同点: 两者都是强缓存。

不同点

  • Expires是http1.0规定的,而Cache-Control是http1.1规定的。
  • Expires的过期时间采用的是绝对时间,容易造成差错; 而Cache-Control的过期时间采用的时相对时间,在缓存上不会出现问题。
  • 两者可以同时存在于一次请求中,但是不会同时在一次请求中起作用。 在HTTP1.0的环境下,Cache-Control不起作用,Expires起作用; 在HTTP1.1的环境之下, Expires不起作用,而Cache-Control起作用。当前一般都是http1.1的情况,所以Expires是作为一种向下兼容的形式而存在的
  • Cache-Control的选择更多,功能更为强大,推荐使用。 Expires作为强缓存,功能单一,不推荐使用。


三、协商缓存header

协商缓存一般是使用if-modified-since/last-modifiedf-none-match/etag 由服务器来决定浏览器缓存的资源是否可以使用。

1、if-modified-since/last-modified

    1. 在用户请求资源之后,服务器会返回这个资源,并且在response header中返回一个 last-modifed 字段,这时浏览器就会缓存这个资源以及最后的修改时间, 可以是: last-modified: Fri, 18 Aug 2017 07:27:24 GMT
    2. 接着,当用户再次请求相同的资源时,需要在请求头中添加 if-modified-since 字段,这个字段的值就是之前存储的last-modifed 的值,服务器得到 if-modified 值之后,会和资源最近的修改时间作比较,如果命中,则返回304,让浏览器使用缓存的资源;否则,返回一个最新的资源并且在 last-modified 修改为最近的资源修改时间。


2、if-none-match/etag

    1. 在用户请求到资源之后,会返回这个资源,并且在response heade 中返回一个 etag 字段,即 entity tag,这个字段的值是一个字符串,唯一的标识了这个资源,只要资源发生了变化,这个etag值就会发生变化
    2. 当用户再次请求资源时,会在request header中携带 if-none-match 字段,其值为上次缓存的 etag值,如果命中,则返回304,使用缓存资源;否则,服务器返回最新的资源。


3、两种协商缓存机制的比较

相同点: 都是为了协商缓存。

不同点

  • 在精度上,Etag优于last-modified。 如果一个文件在1s内改变了很多次,通过etag是可以判断出来并返回最新的资源的,但是last-modifed的精度只能到s,是无法返回最新资源的,准确地说,UNIX记录只能精确到s。
  • 在准确率上,Etag优于last-modified。有些文件可能整体copy等,只是在时间上发生了变化,而内容上并没有发生变化(etag变化,last-modified不变),如果使用last-modified,那么就会返回最新的资源,实际上这是不需要的。
  • 在性能上,last-modified优于Etag。因为last-modified只需要记录时间,而etag需要重新由服务器生成一个hash值,所以在性能上etag略差。
  • 在优先级上,Etag优于last-modified也就是说,etag和last-modified是可以同时使用的,但是到服务器端,会优先判断etag,如果相同,直接返回304;如果不同,就继续比较last-modified,然后再决定是否返回新的资源。


四、浏览器缓存过程

img

  • 浏览器第一次加载资源,服务器返回200, 浏览器将资源下载下来,把资源和response header相关内容一并缓存。
  • 下一次加载时,首先比较cache-control,如果没有超过时间,则命中强缓存,不发送请求,直接读取本地文件(如果不支持http1.1,则使用expires来判断);如果时间已经过期,则发送带有if-none-matchif-modified-since的请求头。
  • 服务器接受到请求之后,首先判断etag是否和服务器上文件的etag一致,如果一致,则命中协商缓存,返回304;如果不一致,返回新的资源并带上新的etag值返回200。
  • 如果请求中没有etag值,则比较发送来的if-modified-since 值,如果命中,则返回304,;否则,返回新的资源带上新的last-modified的值并返回状态码200。


五、用户行为与缓存类型 

  1. 地址栏访问,链接跳转是正常用户行为,将会触发浏览器缓存机制
  2. F5刷新,浏览器会设置max-age=0跳过强缓存判断,进行协商缓存判断
  3. ctrl+F5刷新,跳过强缓存和协商缓存,直接从服务器拉取资源