导语
本文向大家要为大家讲解的是关于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 为准
参考
掘金小册《前端性能优化原理与实践》
通俗易懂 值得一读
结语
前端界的一枚小学生!!!