HTTP缓存那些事

现如今,作为一个前端工程师,无论你去哪家公司面试都会被问到 cache controll , 304等相关的面试题,今天小编就带着你一起来系统梳理下浏览器缓存里的那些事。废话不多说,先给大家上图:

HTTP缓存那些事_第1张图片
相信大家看到这张图,有种豁然开朗的感觉。既然我们要梳理浏览器读取缓存的机制,首要的是,我们得先搞清楚浏览器是怎么读取缓存的:

  1. 无请求读取缓存:浏览器从服务器返回的缓存过期时间(cache-control: max-age=300,服务端在response的时候设置到响应头内)判断得出。如果文件的缓存还没有过期,就直接从缓存文件夹读取缓存文件,显示网页,并没有走任何网络请求。
  2. 无修改读取缓存: 浏览器发送HTTP请求请求头中包含 If-Modified-SinceIf-None-Match 字段,让服务器判断是否应该读缓存文件,如果服务器判断文件没有变化,就会返回所谓的 304 ,并且没有响应实体。这时候浏览器就会读取缓存文件。

接下来我们就一一梳理从页面加载,到获取最终资源的各类情况:

Ctrl + F5(win)|| cmd + shift + R (mac) vs F5(win)|| cmd + R (mac) || 新开窗口
  • F5 触发的 HTTP 请求的请求头中通常包含If-Modified-SinceIf-None-Match 字段,或者两者兼有,此时如果服务器判断文件有没有发生变化,就会返回 304 响应, 也就没有跳过缓存;

  • Ctrl + F5 触发的 HTTP 请求的请求头中通常包含Pragma: no-cacheCache-Control: no-cache 字段,或者两者兼有,服务器看到 no-cache 这样的值就会把最新的文件响应过去.也就跳过了缓存.

那么问题就来了,服务器又是从何得知文件有没有变化呢?服务器的判断依据是啥?okay,我们再次回到第一张图中看一眼。没错,映入我们眼帘的就是它们 EtagLast-Modified(最新修改时间)。

地址栏回车、浏览器前进后退、页面链接跳转

在此,我们先需要了解两个概念:

Etag: 实体标签(EntityTag)是唯一标识了一个组件的一个特定版本的字符串,是 web服务器 用来确认组件缓存组件有效性的一种机制,通常使用组件的某些属性来构造它。用于检测浏览器缓存中的组件与原始服务器上的组件是否匹配

Last-Modified: 原始服务器(部署我们代码的服务器)通过 Last-Modified 响应头来返回组件的最新修改时间。

当我们在地址栏回车、浏览器前进后退、页面链接跳转中进行期中任何一个操作时,浏览器都会去确认是否有缓存,在缓存没有过去的情况下(也就是 Cache-ControlmaxAgeExpires 没有失效的情况下)会直接浏览器缓存,也就是所谓的无请求读取缓存。接下来就是重点了,擦亮你的金丝边眼镜看好了: 当没有缓存或者缓存过期的情况下, 浏览器发送请求头中包含 If-Modified-Since:服务器资源上次更新时间If-None-Match:etag 字段的HTTP请求。来个例子,让大家看的更直白些;
eg:没有缓存的请求

// request
GET /i/yahoo/gif HTTP 1.1
Host: us.yimg.com

// response

HTTP 1.1 200 OK

Last-Modified:Tue,12 Dec 200603:03:59 GMT

ETag:10c24bc-4ab-457elc1f“

在此请求相同组件

// request
HTTP 1.1

Host: us.yimg.com

If-Modified-Since:Tue,12 Dec 200603:03:59 GMT

If-None-Match:10c24bc-4ab-457elc1f“

// response
HTTP 1.1 304 Not Midified

那么这时候,又有人要问了,既然能拿到服务器资源最后一次的更新时间,为什么还要引入 Etag,只要新的需求发布了,我能确保能拿到最新的资源不就行了么!why Etag?

  • Etag 主要是为了解决 Last-Modified 无法解决的一些问题:
  1. 一些文件页也许会周期性更改,但是他的内容并不变化(只是修改时间会变),这个时候我们就不希望客户端认为这个文件被修改了,二重新拉去最新的资源。
  2. If-Modified-Since 能检查的粒度是妙级的,这种修改无法判断,这种修改通过 Last-Modified 是不会变化的。(或者说UNIX记录MTIME只能精确到秒)
  3. 某些服务器不能精确地得到文件的最后修改时间
  • Etag 存在的潜在问题
    上边定义也提到了, Etag 使用组件的某些属性来构造它,但是有些属性对于特定的部署了网站的服务器来说是唯一的。那么当使用集群服务器的时候,浏览器从一台服务器获取了原始组件,之后又向另外一台服务器发起条件GET 请求,Etag 就会出现不匹配的情况.

  • 解决 Etag 潜在问题的最佳事件方案

  1. 如果只使用 Last-Modified 不会有问题的情况下,可以考虑移除。
  2. 确定使用 Etag 的话,在配置的时候,移除能够影响到组件集群服务器验证的属性,例如只包含组件大小和时间戳。
缓存去了哪里

浏览器可以在内存、硬盘中开辟一个空间用以保存请求资源的副本。我们经常在 Dev tool 里面看到 Memory Cache (内存缓存) 和 Disk Cache (硬盘缓存),值得就是缓存的位置。

请求一个资源时,会按照 Server Worker -> Memory Cache -> Disk Cache -> Push Cache -> Push Cache 依次查找缓存,如果命中则使用缓存,否则发起网络请求。

  • 内存缓存 - 200 from Memory Cache
    • 读取速度快,持续时间短(关闭进程,缓存资源随之销毁),容量小
  • 硬盘缓存 - 200 from Disk Cache
    • 读取速度慢,持续时间长(关闭进程,缓存资源依然存在),容量大
友好的缓存策略

对于缓存的使用,很多人真是的爱恨交加。开发的时候,总是希望立即生效? 上线时又希望文件尽可能的被缓存,来提高性能。特别是对缓存机制还不清楚的情况下,更是痛不欲生。现在我们已经有所了解了,那就应该趁热打铁,打造一个更好的缓存策略是首当其冲的紧要事情了。

  • 为不经常改变的资源设置较长时间的缓存
    • 第三方库
    • 图片
    • 字体文件
  • 给文件名设置唯一标识,确保文件修改生效
    有些时候为了解决 bug,我们可能会修改一些文件,比如应用的 CSS、JS 等。如果这些文件已经被缓存,那么除非用户强制刷新页面,否则用户只有在缓存过期之后才有可能获取新的文件。如何让浏览器不使用缓存,而是重新下载新的文件呢?有一个办法就是给文件名加上唯一标识,比如 Hash 或版本信息。当文件修改之后,这个唯一标识也会随之改变。浏览器发现文件改变之后,就不会使用缓存了。
  • 明确一些资源不能被缓存
    比如一些敏感数据,如果不应该被浏览器缓存,需要在 Response Header 中设置 Cache-Control: no-store。
写在最后

到此,HTTP缓存那些事就先介绍到这里了,以上是个人实际开发中遇到的问题,进行的一系列梳理,希望有助于大家,不对的地方还望大神多多指正。

你可能感兴趣的:(前端技术知识)