浏览器的缓存机制

397 阅读6分钟

导语

本文向大家要为大家讲解的是关于HTTP 缓存机制,HTTP缓存是我们最为熟悉的一种缓存机制.他又分为强缓存协议缓存.优先级较高的是强缓存.

强缓存

强缓存是利用http头中的Expires和Cache-Control两个字段来控制的.强缓存中当请求再次发出中,浏览器会根据其中的exprise和cache-control判断目标资源是否"命中"强缓存.若命中则直接从缓存中获取资源,不会再与服务器发生通讯.

命中强缓存的情况下 ,返回的HTTP状态码为200

强缓存的实现 从expires到cache-control

实现强缓存,之前一直使用expires. 当服务器返回响应式,在Response Headers 中将过期时间写入expires字段 ,如:

expires: Wed, 11 Sep 2019 16:12:18 GMT

其实 expires就是一个时间戳,当之后再试图向服务器请求资源,浏览器就会先对比本地时间和expires的时间戳,如果本地时间小于expires设定过期时间,那么就会直接去缓存取这个资源.

相信细心的大家已经看出问题了, 最大的问题在于对'本地时间'的依赖.如果服务器和客户端的时间设置的可能不同,或者直接手动吧客户端的时间改点,那个expires将无法达到我们的预期.

考虑到expires的局限性,HTTP1.1新增了Cache-Control字段来完成expires的任务.

expires能做到的事情,Cache-Chotrol都能做到,expries做不到的 Cache-Controly也能做到,因此Cache-Control 可以视为expires的完全替代方案, 不过也会继续使用expires向下兼容

cache-control 特写如下:

cache-control: max-age=31536000

在Cache-Control中 通过max-age 来控制资源的有效时间.max-age 不是一个时间戳,而是一个时间长度,在本例中,意味着该资源在31536000秒以内都是有效的,完美的规避了时间戳带来的问题.

Cache-Control相对于expires更加准确,他的优先级更高. 当他们同时出现时,以Cache-Control为准.

Cache-Control 应用分析

cache-control: max-age=3600, s-maxage=31536000

文中 s-maxage优先级高于max-age,两者同时出现时,优先考虑s-maxage.如果s-maxage 未过期,则向代理服务器请求其缓存内容. 其中s-maxage仅在代理服务器生效,客户端只考虑max-age

public 与 private

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

如果我们为资源设置了public,那么它即可以被浏览器缓存,也可以被服务器缓存;如果我们设置了private,则该资源只能被浏览器缓存,private为默认值.

no-store与no-cache

no-store绕开了浏览器:我们为资源设置了no-cache后,每一次发送的请求都不会再去询问浏览器的缓存情况,而是直接先服务器去确认资源是否过期(走下文中的协议缓存)

no-cacheb比较绝情, 就是不使用任何缓存策略.在no-cache的基础上,直接绕开服务器的缓存确认.只允许直接服务器发送请求,并下载完整响应.

协商缓存

协议缓存是依赖于服务器与浏览器之间的通讯.

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

如果服务器提示缓存资源未改动(Not Modified),资源会被重定向到浏览器缓存, 这种情况下网络请求的状态码是304

Last-Modified

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

Last-Modified: Fri, 27 Oct 2017 06:35:57 GMT

随着每次请求时,会带上一个叫If-Modified-Since的时间戳字段,他的值正是上一次response返回给他的last-modified值:

If-Modified-Since: Fri, 27 Oct 2017 06:35:57 GMT

服务器接收到这个时间戳后,会对比改时间戳和资源在服务器上的最后修改时间是否一致,从而判断资源是否发生变化,如果发生了变化,就会返回一个完整的响应内容.并在Response Headers 中新添加Last-Modified值;否则,返回304响应,Response Headers 不会在添加Last-Modified字段

常见的两个场景:

  • 我们编辑了文件,但文件的内容没有改变.服务器并不知道我们是否真正改变了文件,他仍然通过最后的编辑时间判断.因此这个资源在再次被请求时,会被当成新资源,从而引发一次完整的响应--不该重新请求时候重新请求了
  • 当我们修改了文件的速度过快时 ,由于If-Modified-Since 只能检查到秒为最小单位的时间差,所以它没感知到这个改动--该重新请求时 反而没重新请求.

这两个场景指向了同一个Bug -- 服务器没有正确的感知文件的变化 . Etag 作为Last-Modified的补充出现了

Etag

是由服务器为每个资源生成唯一的** 标识字符串** 这个标识字符串是基于文件内容编码的,只要文件内容不同,他们对应的Etag就是不同的,反之亦然.因此Etag能够准确的感知文件的变化.

Etag 和 Last-Modified类似,当首次请求时,我们会在响应头里获取到一个最初的标识符字符串

ETag: W/"2a3b-1602480f459"

那个下次请求时,请求头里会带上一个值相同的,名为if-None-Match的字符串供服务器对比:

If-None-Match: W/"2a3b-1602480f459"

Etag的生成过程需要服务器额外付出开销,会影响服务器的性能,这是他的弊端,因此启用Etag需要我们审时度势.它只能作为 Last-Modified 的补充和强化存在.ETag在感知文件变化比Last-Modified更加准确,优先级也更高.当Etag与Last-Modified同时存在时 以ETag 为准

参考

掘金小册《前端性能优化原理与实践》

通俗易懂 值得一读

结语

前端界的一枚小学生!!!