缓存策略的分类:
- 强缓存
- 协商缓存
缓存策略都是通过 http header 来实现的。
浏览器每次发起请求,都会现在浏览器中查找该请求的结果以及缓存标识。
浏览器每次拿到请求的结果,都会将该结果和缓存标识存入浏览器缓存中。
一、强缓存
强缓存:不会向服务器发起请求,直接从缓存中读取数据,在 chrome 控制台的 Network 选项中可以看到该请求返回 200 状态码,并且 Size 显示 from disk catch 或 from memory cache。强缓存可以设置两种 HTTP Header 实现:Expires 和 Catch-Control。
1.Expires:
缓存过期时间,用于指定资源到期时间,是服务端的具体时间点。也就是说,Expires=max-age + 请求时间,需要和 Last-Modified 结合使用。Expires 是 web 服务器响应消息字段,在相应 http 请求时告诉浏览器在过期时间前,可以直接从浏览器缓存读取数据,而不需要再次请求。
Expires 是 HTTP/1 的产物,受限于本地时间,可能会造成缓存失效(即需要服务端和客户端时间严格一致)。Expires:Wed,22 Oct 2018 08:41:00 GMT 表示资源会在 Wed,22 Oct 2018 08:41:00 GMT 后过期,需要再次请求。
2.Catch-Control:
在 HTTP/1.1中,Catch-Control 是最重要的规则,主要用于控制网页缓存。比如当 Catch-Control:max-age=300 时,则代表在这个请求正确返回时间(浏览器也会记录下来)的 5 分钟内再次加载资源,就会命中强缓存。Catch-Control 可以在请求头或者响应头中设置,并且可以组合使用多种指令:
3.Expires 和 Catch-Control 两者对比
其实这两者差别不大,区别在于 Expires 是 HTTP/1.0 的产物,Catch-Control 是 HTTP/1.1 的产物,两者同事存在时,Catch-Control 优先级高于 Expires;在某些不支持 HTTP/1.1 的环境下,Expires 就会发挥用处。所以 Expires 其实是过时的产物,现阶段他的存在只是一种兼容性的写法。强缓存判断是否缓存的依据来自于是否超出某个时间或者某个时间段,而不关心服务端文件是否已经更新,这可能导致加载文件不是服务端最新的内容,那我们如何获知服务端的内容是否已经发生了更新呢?此时我们需要用到协商缓存策略。
二、协商缓存
协商缓存就是强缓存失效后,浏览器携带缓存标识向服务器发起请求,由服务器根据缓存标识决定是否使用缓存的过程,主要有以下两种情况:
- 协商缓存生效,返回 304 和 Not Modified
- 协商缓存失效,返回 200 和请求结果
协商缓存可以通过设置两种 HTTP Header 实现:
- Last-Modified
- Etag
1.Last-Modified 和 If-Modified-Since
浏览器在第一次访问资源时,服务器返回资源的同时,在 response header 中添加 Last-Modified 的 header,值是这个资源在服务器上的最后修改时间,浏览器接收后缓存文件和 header:
Last-Modified: Fri, 22 Jul 2016 01:47:00 GMT
浏览器下一次请求这个资源时,浏览器先检测到有 Last-Modified 这个 header,于是添加 If-Modified-Since 这个 header ,值就是 Last-Modified 的值;服务器再次收到这个资源的请求,会根据 If-Modified-Since 的值于服务器中这个资源的最后修改时间作对比,如果没有变化,返回 304 和空的响应体,直接从缓存读取,如果 If-Modified-Since 的时间小于服务器中这个资源的修改时间(值不一致),说明该资源有更新,于是返回新的资源文件和 200。
但是 Last-Modified 存在一些弊端:
- 如果本地打开缓存文件,即使没有对文件进行修改,但还是会造成 Last-Modified 被修改,服务端不能命中缓存导致返回相同的资源文件和 200。
- 因为 Last-Modified 只能以秒计时,如果在不可感知的时间内修改了文件,那么服务端会认为资源还是命中了,不会返回正确的资源。
既然根据文件修改时间来决定是否命中缓存尚有不足,能否可以直接根据文件内容是否修改来决定缓存策略呢?所以在 HTTP/1.1出现了 ETag 和 If-None-Match
2.ETag 和 If-None-Match
ETag 是服务器响应请求时,返回当前资源的一个唯一标识(由服务器生成)只要资源有变化,Etag 就会重新生成。浏览器在下一次加载资源向服务器发起请求时,会将上一次返回的 ETag 值放到 request header 的 If-None-Match 中,服务器只需要对比客户端传来的 If-None-Match 跟自己服务器上该资源的 ETag 是否一致,就能很好的判断该资源相对于客户端而言是否被修改过了。如果服务器发现 ETag 匹配不上,那么直接以常规 GET/POST 200 回包的形势将新的资源(当然也包括了新的 ETag)发送给客户端;如果 ETag 一致,则直接返回 304 知会客户端直接使用本地缓存即可。
3.Last-Modified 和 ETag 的对比
- 首先是精度上,ETag 要优于 Last-Modified 。Last-Modified 的时间单位是秒,如果某个文件在1秒内改变了多次,那么它的 Last-Modified 其实并没有体现出来修改,但是 ETag 每次都能确保了精度;如果是负载均衡的服务器,各个服务器生成的 Last-Modified 也有可能不一致。
- 第二在性能上,ETag 要逊于 Last-Modified ,毕竟 Last-Modified 只需要记录时间,而 ETag 需要服务器通过算法来计算出一个 hash 值。
- 第三在优先级上,服务器校验优先考虑 ETag。
三、缓存机制
强缓存优先于协商缓存进行,若强缓存(Expires 和 Catch-Controe)生效则直接使用缓存,若不生效则进行协商缓存(Last-Modified/If-Modified-Since 和 ETag/If-None-Match),协商缓存由服务器决定是否使用缓存,若协商缓存失效,那么代表该请求的缓存失效,返回 200,重新返回资源和缓存标识,再存入浏览器缓存中;生效则返回 304,继续使用缓存。
四、强缓存与协商缓存的区别
四、用户行为对缓存的影响
五、from memory catch 和 from disk catch 的对比
在 chrome 浏览器中的控制台 Network 中 size 栏通常会有三种状态:
1.from memory catch
2.from disk catch
3.资源本身的大小(如1.5k)
三种的区别:
1.from memory catch:字面理解是从内存中,其实也是字面含义,这个资源是直接从内存中拿到的,不会请求服务器,一般已经加载过该资源且缓存在了内存当中,且当关闭该页面时,此资源就被内存释放掉了,再次打开相同页面时,不会出现 from memory catch 的情况。
2.from disk catch :同上类似,此资源是从磁盘中取出的,也是在已经在之前的某个时间加载过该资源,不会请求服务器。但是此资源不会随着页面关闭而释放掉,因为是存在于内存当中的,下次打开任然会 from disk catch。
3.资源本身大小数值:当 HTTP 状态为 200 是实实在在的从服务器获取的资源,当 HTTP 状态为 304 时该数字是与服务器端通信报文的大小,并不是该资源本身的大小,该资源是从本地获取的。