为了账号安全,请及时绑定邮箱和手机立即绑定

HTTP 缓存

前提

浏览器通过网络下载各种各样的资源,例如 HTML、CSS、JavaScript、图片、视频、音频、字体等等,这是浏览器工作过程的第一步,也是非常耗时的一步。其实,很多静态文件,如 HTML 文本、图片等,这些资源不经常发生变化,完全可以储存在客户端( 保存到本地磁盘中 ),以便下次使用。因此,浏览器引入了资源缓存机制,也叫 HTTP 缓存机制。

基本概念

HTTP 缓存的基本思想是在客户端建立一个资源的缓存池( 以下简称资源池 ),所有对资源的请求都会优先获取缓存中的信息,以决定是否需要向服务器提出资源请求。

示意图:

图片描述

查询方式

资源的唯一性是通过 URL 来标记的,因此从资源池中查询资源的关键字就是 URL。如果两个资源有不同的 URL,但它们的内容完全一样,也会被认为是两个不同的资源。

生命周期

由于磁盘存储的空间是有限的,因此,资源池会清除那些“最近最少使用”的资源。

强制缓存

在已缓存的资源未失效( 未过期 )的情况下,不会向服务器发送请求,会直接从资源池中读取资源。

资源未失效示意图:

图片描述

资源已失效示意图:

图片描述

缓存规则

对于强制缓存而言,响应头中有两个字段标明了它的缓存规则,一个是 Expires,另一个是 Cache-Control。

Expires

Expires 的值包含具体的日期和时间,是服务器返回的资源到期时间, 即在此时间之前,可以直接从资源池中读取资源,无需再次请求服务器。

例子:

Expires: Wed, 31 Jul 2019 02:58:24 GMT

Cache-Control

Cache-Control 可以被用于请求和响应头中,组合使用多种指令来指定缓存机制。下面列举了比较常用的指令:

  • private:资源只可以被客户端缓存,Cache-Control 的默认值。
  • public:资源可以被客户端和代理服务器缓存。
  • max-age=t :客户端缓存的资源将在 t 秒后失效。
  • no-cache:需要使用对比缓存来验证资源( 下面会详细介绍 )。
  • no-store:不缓存任何资源。

private 和 public 的区别在于是否允许中间节点( 即代理服务器 )进行资源缓存,如果 Cache-Control 值设置为 public:

客户端 <--> 代理服务器 <--> 服务器

中间的代理服务器可以缓存资源,如果下一次再请求同一资源,代理服务器就可以直接把自己缓存的资源返回给客户端,而无需再请求服务器。但如果 Cache-Control 值设置为 private,表明资源只能被当前用户在客户端缓存,属于私有缓存,不能作为共享缓存( 代理服务器缓存的资源可以共享 )。

例子:

Cache-Control: max-age=31536000

例子中,Cache-Control 仅指定了 max-age,所以默认为 private,缓存时间为 31536000 秒( 365天 )。也就是说,在 365 天内再次请求该资源时,都会直接使用资源池中的资源。

Expires 与 Cache-Control 同时出现

Expires 属于 HTTP/1.0 的产物,而 Cache-Control 属于 HTTP/1.1 的产物,如果服务器同时设置了 Expires 与 Cache-Control,那么以更先进的 Cache-Control 为准。在某些不支持 HTTP/1.1 的环境中,Expires 才能发挥作用,现阶段只是一种兼任性的写法。

强制缓存的弊端

强制缓存的判定标准,主要依据来自于是否超出某个时间或者某个时间段,而不关心服务器端资源是否已经更新,这可能会导致加载的资源早已不是服务器端最新的内容。

对比缓存( 协商缓存 )

针对强制缓存的弊端,对比缓存需要进行资源比对来判断是否可以使用缓存。

客户端第一次请求资源时,服务器会将缓存标识与资源一起返回给客户端,客户端将二者备份至资源池中。当再次请求相同资源时( 此时,强制缓存期限已到,缓存资源还在 ),客户端将备份的缓存标识发送给服务器,服务器根据缓存标识进行验证,如果验证结果为未更新,服务器会返回 304 状态码,通知客户端可以继续使用缓存资源。

有一点需要特别注意:对比缓存的优先级低于强制缓存,因此只有在强制缓存失效后,客户端才会携带缓存标识向服务器发起请求。

资源未更新示意图:

图片描述

资源已更新示意图:

图片描述

缓存规则

不同于强制缓存,对比缓存最重要的是缓存标识的传递。缓存标识在请求头和响应头之间进行传递,具体方式可以分为以下两组:

Last-Modified / If-Modified-Since

当我们第一次发出请求时,Last-Modified 由服务器返回,通知客户端,该资源的最后修改时间。当我们再次请求该资源时,If-Modified-Since 由客户端发送,其保存了 Last-Modified 的值。服务器收到请求后,将 If-Modified-Since 的值与被请求资源的最后修改时间进行比对。若资源的最后修改时间大于 If-Modified-Since 的值,说明资源被修改过,则返回状态码 200 以及最新资源;若资源的最后修改时间小于或等于 If-Modified-Since 的值,说明资源无修改,则返回状态码 304,通知客户端继续使用缓存资源。

例子:

//第二次发出请求时,请求头内容
If-Modified-Since: Mon, 23 Jul 2018 08:29:29 GMT

//第二次发出请求时,响应头内容
Last-Modified: Mon, 23 Jul 2018 08:29:29 GMT

//此时,状态码应该为:
Status Code: 304 Not Modified

ETag / If-None-Match

当我们第一次发出请求时,ETag 由服务器返回,其值为该资源的标签。当我们再次请求该资源时,If-None-Match 由客户端发送,其保存了 ETag 的值。服务器收到请求后,将 If-None-Match 的值与被请求资源的标签进行比对。若资源的标签不等于 If-None-Match 的值,说明资源被修改过,则返回状态码 200 以及最新资源;若资源的标签等于 If-None-Match 的值,说明资源无修改,则返回状态码 304,通知客户端继续使用缓存资源。

ETag / If-None-Match 组合的优先级高于 Last-Modified / If-Modified-Since 组合。

例子:

//第二次发出请求时,请求头内容
If-None-Match: W/"5ce-164c641f628"

//第二次发出请求时,响应头内容
ETag: W/"5ce-164c641f628"

//此时,状态码应该为:
Status Code: 304 Not Modified

应用缓存机制

结合以上强制缓存和对比缓存的内容,我们可以大概绘制一份浏览器使用缓存的流程图:

图片描述


如有错误,欢迎指正,本人不胜感激。

点击查看更多内容
6人点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消