4

引言

记得当年面试创新工场前端实习生时,面试官曾经给了我一个建议,让我有空去了解一下浏览器缓存机制,很有意思的。当时的我因为拿到了实习 offer 之后就把这句话忘在了脑后。直到校招季,就在面试即将结束的时候,面试官微笑着问我“能说一下浏览器缓存的知识吗?”然后不负众望,面试官笑容渐渐消失,回家后等了几天收到了感谢信。
学习的过程就是从不会到会,花了一个小时时间拜读网上的相关博客,终于对浏览器缓存有了初步认识,开篇文章记录下来,加深学习印象,也分享给也许现在不明白但是马上就要明白的你。

什么是浏览器缓存

通俗地讲,浏览器缓存就是把曾经访问过的资源拷贝一份副本存在浏览器中,当以后再次访问该网站时会根据一系列判断最终决定是从浏览器中直接将曾经存储的副本拿出来还是去服务器获取。
那么使用浏览器缓存的原因也就很明显了:

  1. 减少服务器压力
  2. 减少网络带宽消耗
  3. 提升页面打开速度

过程

我们可以简单思考一下,在什么情况下我们会去直接使用浏览器缓存呢?
首先,我们要有相应的缓存。
其次,我们的缓存没有过期。
(上面两句是我不负责任的回答,具体流程请看下面)

这里我们借用网络上的一张图片来大概了解一下过程:
图片描述

当我们第一次打开某网站时,我们的本地是不存在相应的缓存的,于是我们需要向服务器请求对应的资源,根据返回的内容,判断是否将资源缓存在本地,最后将页面呈现出来。

当我们再次访问该网站时,我们发现本地有相应的缓存,于是我们先要判断该缓存是否过期,如果没有,则从缓存中读取内容。如果过期,我们会发送请求判断是否使用该缓存,如果不使用,则向服务器请求资源,再根据情况判断是否更新本地缓存。

根据上面大致的流程,我们将要引出两个非常重要的概念。

强缓存和协商缓存

强缓存

用户发送的请求,直接从客户端缓存读取,不发送请求到服务器,不与服务器发生交互行为。

协商缓存

用户发送的请求,发送到服务器后,由服务器判断是否存缓存中获取资源。若结果为是,则返回 304 ;若不是,则返回 200 。

共同点:客户端获得得数据都是从客户端缓存中获取。
不同点:强缓存不与服务器发生交互,而协商缓存需要与服务器交互。

过程详解

判断浏览器是否有缓存

我们前面也简单说了,所谓的“浏览器缓存”其实就是用户的本地资源,不同浏览器的缓存文件地址也不相同。
通俗的讲,判断浏览器是否有缓存就是去判断本地是否有对应的文件。
(这里不作重点所以介绍的非常简单,如有兴趣可百度不同的浏览器缓存位置自行查看)

判断缓存是否过期

浏览器在对资源缓存过后,会将服务器端当时的 response header 保留下来,如下图所示:
图片描述
其中 Date 字段表示此次缓存时服务器的时间。
其中标红的两个字段是该过程的重点: Expires 和 Cache-Control

Expires

这是 HTTP1.0 中的标准,表示为过期时间(服务器时间)

Cache-Control

这是 HTTP1.1 中的标准,表示为相对的过期时间。单位为秒。有如下属性:

  1. max-age: 设置缓存的最大的有效时间。存在 max-age 会覆盖掉 Expires
  2. s-maxage: 只用于共享缓存,比如CDN缓存(s -> share)。与 max-age 的区别是:max-age 用于普通缓存,而 s-maxage 用于代理缓存。如果存在 s-maxage ,则会覆盖 max-age 和 Expires
  3. public:响应会被缓存,并且在多用户间共享。默认是 public
  4. private: 响应只作为私有的缓存,不能在用户间共享。如果要求HTTP认证,响应会自动设置为private
  5. no-cache: 指定不缓存响应,表明资源不进行缓存。但是设置了 no-cache 之后并不代表浏览器不缓存,而是在缓存前要向服务器确认资源是否被更改。因此有的时候只设置 no-cache 防止缓存还是不够保险,还可以加上 private 指令,将过期时间设为过去的时间
  6. no-store: 绝对禁止缓存
  7. must-revalidate: 如果页面过期,则去服务器进行获取

我们可以发现, HTTP1.0 标准中的 Expires 存在一个问题:如果本地时间和服务器时间不一致,就会出现偏差。于是在 HTTP1.1 中使用了相对时间 Cache-Control 来弥补这个问题。

于是乎,判断缓存是否过期的步骤就是:

  1. 查看是否存在 Cache-Control 中的 max-age / s-maxage ,如果有,则用 Date 值与其相加计算出过期时间,将当前时间与过期时间进行比较判断是否过期。
  2. 查看是否存在 Cache-Control 中的 max-age / s-maxage ,如果没有,则用 expires 作为过期时间比较。

这里执行完毕后,如果判断结果为没有过期,则使用客户端缓存,也就是命中“强缓存”。

是否使用缓存

如果通过上述过程发现缓存过期了,这里我们就要与服务器协商是否使用缓存。
看上面的图,这时我们需要注意的是 ETag 和 Last-Modified 属性:

ETag

资源的标识。每个文件不相同,主要用于区分文件是否相同。

Last-modified

请求的资源上次的修改时间。

我们会向服务器发送请求,如果上一次的缓存中存在以上两个属性,那么浏览器会在 request header 中加入 If-Modified-Since (值为 Last-modified 的值)和 If-None-Match (值为 ETag 的值)。
这两个值分别为客户端保留的资源上次修改时间和资源的标识。

如果没有变化,则命中“协商缓存”,返回 304

用户行为对缓存的 影响

图片描述

结束语

以上就是浏览器缓存相关的入门知识,如果哪里有描述不正确请及时告知我,非常感谢,同时希望这篇文章能对你有所帮助。

参考资料:
彻底理解浏览器缓存机制
浏览器的协商缓存与强缓存

Miyang
197 声望10 粉丝

快手前端工程师