浏览器缓存机制

1,171 阅读8分钟

开发的时候,我们常常会遇到因为缓存而导致页面修改没有及时展示的情况,这个时候我们不禁就会思考,为什么有时候页面缓存了,有时候页面没有缓存,究竟如何正确使用缓存,如何快速定位问题是否由缓存导致,本文我们就来深入理解下浏览器的缓存机制。

缓存简介

所谓缓存,就是浏览器将一些资源存放在用户磁盘或内存中的行为。根据缓存存放的位置不同,可以将缓存分为 内存缓存硬盘缓存, 根据缓存的策略不同,可以分为 强制缓存协商缓存

浏览器与服务器通信的方式为应答模式,即是:浏览器发起HTTP请求 – 服务器响应该请求。那么浏览器第一次向服务器发起该请求后拿到请求结果,会根据响应报文中HTTP头的缓存标识,决定是否缓存结果,是则将请求结果和缓存标识存入浏览器缓存中,简单的过程如下图

  • 浏览器发起请求,检查缓存中是否含有所需资源和缓存标识
  • 当没有需要的缓存资源,则向服务器发起请求
  • 服务器返回资源
  • 浏览器根据缓存标识决定是否需要将资源保存在缓存中

下面我们先来看看 内存缓存硬盘缓存

内存缓存/硬盘缓存

内存缓存(from memory cache)

内存缓存具有两个特点,分别是快速读取和时效性:

  • 快速读取:内存缓存会将编译解析后的文件,直接存入该进程的内存中,占据该进程一定的内存资源,以方便下次运行使用时的快速读取。
  • 时效性:一旦该进程关闭,则该进程的内存则会清空。

硬盘缓存(from disk cache)

硬盘缓存则是直接将缓存写入硬盘文件中,读取缓存需要对该缓存存放的硬盘文件进行I/O操作,然后重新解析该缓存内容,读取复杂,速度比内存缓存慢。

两者比较

浏览器读取命中强缓存资源的顺序为memory –> disk:

  • 先去内存看,如果有,直接加载;
  • 如果内存没有,则取硬盘获取,如果有直接加载;
  • 如果硬盘也没有,那么就进行网络请求;
  • 加载到的资源缓存到硬盘和内存。
类型 时效性 容量 存放内容
内存缓存 进程关闭,内存清空 脚本、字体、图片等
硬盘缓存 时效长 CSS等

由上可以看出内存缓存中大多存放JS、图片等资源,而硬盘缓存中大多存放CSS资源以及容量较大的资源,这是因为:

  • CSS样式加载一次即可渲染出网页。
  • 脚本可能随时会执行,如果脚本在磁盘当中,在执行该脚本需要从磁盘中取到内存当中来。这样的IO开销是比较大的,有可能会导致浏览器失去响应。

强制缓存

强制缓存就是向浏览器缓存查找该请求结果,并根据该结果的缓存规则来决定是否使用该缓存结果的过程。

三种情况

  • 不存在该缓存结果和缓存标识,强制缓存失效,则直接向服务器发起请求(跟第一次发起请求一致),如下图

  • 存在该缓存结果和缓存标识,但该结果已失效,强制缓存失效,则使用协商缓存(暂不分析),如下图

  • 存在该缓存结果和缓存标识,且该结果尚未失效,强制缓存生效,直接返回该结果,如下图

Expires

Expires是HTTP/1.0控制网页缓存的字段,其值为服务器返回该请求结果缓存的到期时间,即再次发起该请求时,如果客户端的时间小于Expires的值时,直接使用缓存结果。 到了HTTP/1.1,Expire已经被Cache-Control替代,原因在于Expires控制缓存的原理是使用客户端的时间与服务端返回的时间做对比,那么如果客户端与服务端的时间因为某些原因(例如时区不同;客户端和服务端有一方的时间不准确)发生误差,那么强制缓存则会直接失效,这样的话强制缓存的存在则毫无意义。

Cache-Control

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

  • public:所有内容都将被缓存(客户端和代理服务器都可缓存)
  • private:所有内容只有客户端可以缓存,Cache-Control的默认取值
  • no-cache:客户端缓存内容,但是是否使用缓存则需要经过协商缓存来验证决定
  • no-store:所有内容都不会被缓存,即不使用强制缓存,也不使用协商缓存
  • max-age=xxx (xxx is numeric):缓存内容将在xxx秒后失效

Cache-Control的优先级高于Expires,Cache-Control的是相对值,而Expires是绝对值,在不确客户端和服务端时间是否一致的情况下,推荐优先使用Cache-Control。

协商缓存

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

两种情况

  • 协商缓存生效,返回304,如下

  • 协商缓存失效,返回200和请求结果结果,如下

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生效。

完整缓存流程

  • 请求访问缓存
  • 若存在Expires和Cache-Control,则进行强制缓存
  • 若存在Etag / If-None-Match,则进行协商缓存
  • 若存在Last-Modified / If-Modified-Since,则进行协商缓存
  • 若不存在缓存标识,则向服务器请求资源,并将返回资源及缓存标志放入缓存中

实际应用

了解了缓存机制,我们就来看看缓存能用来做什么。 在我们网页开发过程中,为了实现各种交互功能和页面的美化,我们会引入不少资源,但是在反复加载的过程中,如果我们不断向服务器去请求这些资源,会降低网站的性能,尤其当这些资源的容量较大时,这一性能缺陷就会更为明显,所以我们可以利用设置缓存标志,将那些不常更新,容量较大的资源保存在缓存里,加快网站的再次渲染。

  • 在服务器设置Etag / If-None-Match或Last-Modified / If-Modified-Since,采用协商缓存,可以减少从服务器返回资源的次数,但还是需要多次请求访问服务端
  • 在服务器设置Expires或Cache-Control,直接从缓存中获取资源,更大提升了性能

以上就是有关缓存机制的全部内容,有理解不到位的地方,欢迎各位大佬指正。

参考链接: 彻底理解浏览器的缓存机制 前端性能优化-HTTP添加Expires头