深耕系列之浏览器缓存

436 阅读9分钟

引入

不知你是否有以下疑问:

  1. 为什么第一次访问站点时,打开速度很慢,当再次访问时,速度就很快了。
  2. 明明已经更换了图片,为什么用户看到的图片还是旧的。
  3. 浏览器缓存如何设置,有哪些方法。
  4. 按ctrl + f5 或 勾选上diabled cache,浏览器做了哪些工作。
  5. 明明已经浏览器已经勾选上diabled cache,为什么看到的还是旧的资源。
  6. 如何快速判断资源是否有更新。
  7. 如果没有配置缓存,浏览器是怎么处理资源的

如果你对这些问题还是一知半解的话,那么就需要再深入去理解背后的原理,这些问题都属于浏览器缓存这块的内容,那么让我们来继续学习吧:

什么是浏览器缓存

浏览器缓存是一种在本地保存资源副本,以供下次请求时直接使用的技术。当发现请求的资源在浏览器中有副本时,就会拦截请求,返回对应的副本,不用再去请求服务器,这样做的好处是:

  1. 减少服务器压力。
  2. 网站可实现资源快速加载。

缓存策略

那么浏览器是怎么判断资源是否要缓存,缓存多久。我们都知道,浏览器是通过http请求去获得资源的,那么我们先了解http请求的整个流程(http/1.1版本)

HTTP请求过程

  1. 建立tcp连接
  2. 发送http请求 request.png
  3. 服务器返回请求

response.png 4. 断开连接

从请求过程来看,和浏览器缓存相关的主要是请求报文和响应报文。其实浏览器的缓存策略有两种,强制缓存和协商缓存

  • 强制缓存 就是第一次访问服务器获取数据后,在过期时间内都不会再去重复请求数据,而是去拿浏览器保存的资源副本,核心在于怎么判断资源是否已经过期了。
  • 协商缓存 每次读取数据时都要跟服务器通信,并且会更新缓存标识,服务器收到请求后,会根据缓存标识来判断资源是否有更新,如果没有更新,就返回304状态码,浏览器就读缓存的数据,如果有更新,就返回200状态码,把更新后的资源返回给浏览器。
  • 和缓存相关的请求头和响应头 浏览器采用何种方案,判断资源是否过期,缓存标识是什么,这些都在请求头和响应头中体现,那么和缓存相关的请求头和响应头有哪些呢

HTTP请求头:

Header解释示例
Cache-Control指定请求和响应遵循的缓存机制Cache-Control: no-cache
If-Match请求内容和实体相匹配才生效If-Match: "737060cd8c284d8af7ad3082f209582d"
If-None-Match如果内容未改变返回304代码,参数为服务器先前发送的Etag,与服务器回应的Etag比较判断是否改变If-None-Match: "737060cd8c284d8af7ad3082f209582d"
If-Modified-Since如果请求的部分在指定时间之后被修改则请求成功,未被修改则返回304代码If-Modified-Since: Sat, 29 Oct 2010 19:43:31 GMT

HTTP响应头

Header解释示例
Cache-Control浏览器是否可以缓存及哪种类型Cache-Control: no-cache
ETag请求变量的实体标签的当前值ETag: "737060cd8c284d8af7ad3082f209582d"
Expires响应过期的日期和时间Expires: Thu, 01 Dec 2010 16:00:00 GMT
Last-Modified请求资源的最后修改时间Last-Modified: Tue, 15 Nov 2010 12:45:26 GMT

针对强制缓存

当浏览器向服务端发起请求时,服务端会将缓存规则放入http响应报文里的响应头,控制强制缓存的字段分别是Expires和Cache-Control,其中Cache-Control的优先级比Expires高。

Expires

Expires是HTTP/1.0控制网页缓存的字段,其值为服务器返回该请求结果缓存的到期时间,即再次发起该请求时,如果客户端的时间小于Expires的值时,直接使用缓存结果。

Expires是HTTP/1.0的字段,但是现在浏览器默认使用的是HTTP/1.1,那么在HTTP/1.1中网页缓存还是否由Expires控制? 到了HTTP/1.1,Expires已经被Cache-Control替代,原因在于Expires控制缓存的原理是使用客户端的时间与服务端返回的时间做对比,那么如果客户端与服务端的时间因为某些原因(例如时区不同;客户端和服务端有一方的时间不准确)发生误差,那么强制缓存则会直接失效,这样的话强制缓存的存在则毫无意义.

Cache-Control

在HTTP/1.1中,Cache-Control是最重要的规则,主要用于控制网页缓存,主要取值为:

  • public:表明响应可以被任何对象(包括:发送请求的客户端,代理服务器,等等)缓存
  • private:表明响应只能被单个用户缓存,不能作为共享缓存(即代理服务器不能缓存它),Cache-Control的默认取值
  • no-cache:在发布缓存副本之前,强制要求缓存把请求提交给原始服务器进行验证(协商缓存验证)
  • no-store:缓存不应存储有关客户端请求或服务器响应的任何内容,即不使用任何缓存
  • max-age= :缓存内容将在seconds秒后失效,与Expires相反,时间是相对于请求的时间。
  • must-revalidate:一旦资源过期(比如已经超过max-age),在成功向原始服务器验证之前,缓存不能用该资源响应后续请求。 Cache-Control用法

注:在无法确定客户端的时间是否与服务端的时间同步的情况下,Cache-Control相比于expires是更好的选择,所以同时存在时,只有Cache-Control生效。

针对协商缓存

协商缓存就是强制缓存失效后,浏览器携带缓存标识向服务器发起请求,由服务器根据缓存标识决定是否使用缓存的过程。

Last-Modified / If-Modified-Since

Last-Modified是服务器响应请求时,返回该资源文件在服务器最后被修改的时间 If-Modified-Since则是客户端再次发起该请求时,携带上次请求返回的Last-Modified值,通过此字段值告诉服务器该资源上次请求返回的最后被修改时间。服务器收到该请求,发现请求头含有If-Modified-Since字段,则会根据If-Modified-Since的字段值与该资源在服务器的最后被修改时间做对比,若服务器的资源最后被修改时间大于If-Modified-Since的字段值,则重新返回资源,状态码为200;否则则返回304,代表资源无更新,可继续使用缓存文件

Etag / If-None-Match

Etag是服务器响应请求时,返回当前资源文件的一个唯一标识(由服务器生成) If-None-Match是客户端再次发起该请求时,携带上次请求返回的唯一标识Etag值,通过此字段值告诉服务器该资源上次请求返回的唯一标识值。服务器收到该请求后,发现该请求头中含有If-None-Match,则会根据If-None-Match的字段值与该资源在服务器的Etag值做对比,一致则返回304,代表资源无更新,继续使用缓存文件;不一致则重新返回资源文件,状态码为200

注:Etag / If-None-Match优先级高于Last-Modified / If-Modified-Since,同时存在则只有Etag / If-None-Match生效。 Last-Modified相对与Etag有以下不足之处

  1. Last-Modified标注的最后修改只能精确到秒级,如果某些文件在1秒钟以内,被修改多次的话,它将不能准确标注文件的修改时间。如果1s内文件被修改了,但是Last-Modified没变,此时不能根据Last-Modified判断文件是否被修改过
  2. 如果某些文件会被定期生成,有时内容并没有任何变化,但Last-Modified却改变了,导致文件没法使用缓存
  3. 有可能存在服务器没有准确获取文件修改时间,或者与代理服务器时间不一致等情形

总结

强制缓存优先于协商缓存进行,若强制缓存(Expires和Cache-Control)生效则直接使用缓存,若不生效则进行协商缓存(Last-Modified / If-Modified-Since和Etag / If-None-Match),协商缓存由服务器决定是否使用缓存,若协商缓存失效,那么代表该请求的缓存失效,重新获取请求结果,再存入浏览器缓存中;生效则返回304,继续使用缓存

缓存位置

可以通过开发者工具network的size字段去查看缓存位置,可以知道,分别为from memory cache和from disk cache

那么from memory cache和from disk cache分别是什么,什么时候才会使用到? from memory cache代表使用内存中的缓存,from disk cache则代表使用的是硬盘中的缓存,浏览器读取缓存的顺序为memory –> disk。 在浏览器中,浏览器会在js和图片等文件解析执行后直接存入内存缓存中,那么当刷新页面时只需直接从内存缓存中读取(from memory cache);而css文件则会存入硬盘文件中,所以每次渲染页面都需要从硬盘读取缓存(from disk cache)。 如果关闭标签后再打开页面,那么会从from disk cache获取资源副本

问题回复

  1. 因为浏览器缓存,所以第二次访问站点可以直接从内存或硬盘获取资源副本,提高了资源加载效率。
  2. 可能图片有缓存,走强制缓存,浏览器读取图片副本。
  3. 通过设置http请求头和响应头来设置缓存。有params配置,nginx设置响应头。
  4. 勾选上diabled cache,浏览器会不在携带If-None-Match和If-Modified-Since等参数,强制走http请求获取资源
  5. 虽然浏览器不读取缓存了,但cache-control可能是public,导致代理服务器上仍然有资源副本,代理服务器返回资源副本,特别是cdn。
  6. 可以根据响应头的Last-Modified来判断资源是否更新。
  7. 如果什么缓存策略都没设置,浏览器会采用一个启发式的算法,通常会取响应头中的 Date 减去 Last-Modified 值的 10% 作为缓存时间。