关于 http 缓存,这些知识点你可能都不懂

  • 在地址栏输入 url 回车,强缓存何时失效,何时有效,你知道吗?
  • disk cache 和 memory cache 是什么?在哪里出现?
  • 火狐、IE 的缓存处理策略和 Chrome 同样吗?
  • cache-controlpublicprivate 你知道何时使用吗?
  • 你知道 ETag 可能也有不适用的时候吗?
  • 你知道 http 头中 Vary 和缓存有怎样的关系吗?

若是上面的问题你都懂了,那关掉这个页面吧;若是你喜欢本文,点一个赞吧~css

先小小的回顾下html

http header 描述 强缓存 协商缓存
Pragma 老版本的本地缓存机制,http 1.0 及如下
Expires 在此时候以后,响应过时,时间是绝对时间,受本地时间影响 *
Cache-Control 强缓存策略 Cache-Control: public, max-age=31536000, must-revalidate max-age是相对时间 *
Last-ModifiedIf-Modified-Since 资源最后被更改的时间,精确到秒 *
ETagIf-None-Match 资源的标识值,用来惟一的标识一个资源 *

处理优先级git

在本地 Cache-Control > ExpiresPragma 在不支持 Cache-Control 时生效。github

若是本地缓存过时,则要依靠协商缓存算法

ETag > Last-Modified浏览器

强缓存的 http 状态码是 200 OK缓存

协商缓存的 http 状态码是 304 Not Modifiedbash

Cache-Control

  • public 代表响应能够被任何对象(包括:发送请求的客户端,代理服务器,等等)缓存。
  • private 代表响应只能被单个用户缓存,不能做为共享缓存(即代理服务器不能缓存它)。私有缓存能够缓存响应内容。
  • no-cache 即便有缓存也会向服务器发请求。
  • no-store 让客户端不要把资源存在缓存。
  • max-age=<seconds> 设置缓存存储的最大周期,超过这个时间缓存被认为过时(单位秒)。与 Expires 相反,时间是相对于请求的时间。
  • s-maxage=<seconds> 覆盖 max-age 或者 Expires 头,可是仅适用于共享缓存(好比各个代理),私有缓存会忽略

问题 关于 publicprivate 的区别 服务器

image

翻译成中文:private 不容许代理缓存。举个例子,ISP 服务商能够在你的客户端和互联网之间加上不可见的代理,这个代理会缓存网页来下降带宽,客户端设置 cache-control: private 以后,能够指定 ISP 代理不容许缓存网页,可是容许最后的接受者缓存。而使用 cache-control: public 的意思是说,谁均可以缓存哈,因此中间代理会缓存一份以减小带宽下降费用。网络

若是是谁均可以访问的内容,好比网站 logo,那就用 public 好了,反正也不会有关键数据泄露风险,尽可能中间代理都给缓存上,减小带宽。而若是是一个含有用户信息的页面,好比页面含有个人用户名,那这个页面固然不是对谁都有用,由于不一样的人要返回不一样的用户名嘛,这个时候 private 会适合一些,若是代理缓存了个人这个页面,别的用户访问又会缓存别人的,这显然不合理,并且你的我的私密数据也尽可能不要被保存在不受信任的地方。

固然,全部不被表示为 public 的数据都应该被标识为 private,要否则数据会存储在中间服务器上,别人就有可能会访问到这个数据。

禁止缓存

Cache-Control: no-cache, no-store, must-revalidate

缓存静态资源

Cache-Control:public, max-age=86400

ETag、If-Match

ETagIf-None-Match 常被用来处理协商缓存。而 ETagIf-Match 能够 避免“空中碰撞”

ETag HTTP响应头是资源的特定版本的标识符。这可让缓存更高效,并节省带宽,由于若是内容没有改变,Web服务器不须要发送完整的响应。而若是内容发生了变化,使用 ETag 有助于防止资源的同时更新相互覆盖(“空中碰撞”)。

当编辑 MDN 时,当前的 Wiki 内容被散列,并在响应中放入Etag

ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4 复制代码

将更改保存到 Wiki 页面(发布数据)时,POST 请求将包含有 ETag 值的 If-Match 头来检查是否为最新版本。

If-Match: "33a64df551425fcc55e4d42a148795d9f25f89d4"
复制代码

若是哈希值不匹配,则意味着文档已经被编辑,抛出 412 ( Precondition Failed) 前提条件失败错误。

If-None-Match 是客户端发送给服务器时的请求头,其值是服务器返回给客户端的 ETag,当 If-None-Match 和服务器资源最新的 Etag 不一样时,返回最新的资源及其 Etag

Last-Modified、If-Modified-Since

Last-ModifiedIf-Modified-Since 是资源最后更改的时间。

Last-Modified: <day-name>, <day> <month> <year> <hour>:<minute>:<second> GMT

Last-Modified: Wed, 21 Oct 2015 07:28:00 GMT 
复制代码

这两个的区别是: Last-Modified 是服务器发送给客户端的,If-Modified-Since 是客户端发送给服务器的。

问题 Last-Modified 机制和 ETag 机制的区别和优先级是怎样的?

Last-Modified 只能精确到秒,因此在秒级如下的更改没法检测到。而 ETag 能够表征文件的任何更改,只要文件变化 ETag 就会变化。因此 Last-Modified 是一个备用机制,优先级不如 Etag

客户端请求带有 If-None-Match 在服务端校验匹配则返回 304,校验不匹配则返回 200,同时返回最新的资源和 Etag

Age

Age 消息头里包含消息对象在缓存代理中存贮的时长,以秒为单位。

Age 消息头的值一般接近于0。表示此消息对象刚刚从原始服务器获取不久;其余的值则是表示代理服务器当前的系统时间与此应答消息中的通用消息头 Date 的值之差。

Age: <delta-seconds>
复制代码

age: 1135860
复制代码

Date

Date 是一个通用首部,其中包含了报文建立的日期和时间。

指的是响应生成的时间. 请求通过代理服务器时, 返回的 Date 未必是最新的, 一般这个时候, 代理服务器将增长一个 Age 字段告知该资源已缓存了多久.

Date: Wed, 21 Oct 2015 07:28:00 GMT 
复制代码

Vary

Vary 是一个HTTP响应头部信息,它决定了对于将来的一个请求头,应该用一个缓存的回复(response)仍是向源服务器请求一个新的回复。它被服务器用来代表在 content negotiation algorithm(内容协商算法)中选择一个资源表明的时候应该使用哪些头部信息(headers)。

对于服务器而言, 资源文件可能不止一个版本, 好比说压缩和未压缩, 针对不一样的客户端, 一般须要返回不一样的资源版本。 好比说老式的浏览器可能不支持解压缩, 这个时候, 就须要返回一个未压缩的版本; 对于新的浏览器, 支持压缩, 返回一个压缩的版本, 有利于节省带宽, 提高体验. 那么怎么区分这个版本呢, 这个时候就须要 Vary 了。

服务器经过指定 Vary: Accept-Encoding, 告知代理服务器, 对于这个资源, 须要缓存两个版本: 压缩和未压缩. 这样老式浏览器和新的浏览器, 经过代理, 就分别拿到了未压缩和压缩版本的资源, 避免了都拿同一个资源的尴尬。

Vary: Accept-Encoding,User-Agent
复制代码

如上设置, 代理服务器将针对是否压缩和浏览器类型两个维度去缓存资源. 如此一来, 同一个url, 就能针对 PC 和 Mobile 返回不一样的缓存内容。

怎么让浏览器不缓存静态资源

能够设置 Cache-Control

Cache-Control: no-cache, no-store, must-revalidate
复制代码

也能够给资源增长版本号,这样能够很方便地控制何时加载最新资源,这也是目前作版本更新比较经常使用的手段,即便老资源还在有效期内,加上了 query、hash。

<link rel="stylesheet" type="text/css" href="../css/style.css?version=1.8.9"/>
复制代码

用户行为与缓存

用户按 f5(ctrl+r)、ctrl+f五、点击前进后退 都会触发缓存机制

通过本地测试发现和网上传的有些出入,记录以下(强缓存有效表明直接使用本地的缓存文件,Chrome 状态码为 200,协商缓存有效表明向服务器发起请求,服务器可能返回 304 无内容或者 200 有内容)。

操做 强缓存 协商缓存
页面连接跳转 有效 有效
新开窗口 有效 有效
前进后退 有效 有效
地址栏回车 失效 或者 有效 有效
ctrl+r或者 f5 失效 有效
ctrl+f5 强制刷新 失效 失效

image

地址栏回车和网络上不同,打个比方,若是当前已经在 http://localhost:3333/,而后在地址栏选中后回车,你会发现没有缓存。

image

可是若是当前不在 http://localhost:3333/ ,好比 http://localhost:3333/index.css 或者空白页,而后输入 http://localhost:3333/ 回车,这时候就会直接从本地缓存中读取。

image

惊喜不,意外不

image

关于 memory cache 和 disk cache

这两种缓存类型存在于 Chrome 中。

上个小标题 用户行为与缓存,咱们看到浏览器从本地读缓存的时候有一个 disk cache,与之对应的还有一个 memory cache。看名字也能大概才出来,disk cache 是从硬盘中读取的文件缓存,memory cache 是从内存中直接读取的内容,速度上固然也是后者更快。

那为何要有这两种缓存的形式呢?

disk cache 存在硬盘,能够存不少,容量上限比内容缓存高不少,而 memory cache 从内存直接读取,速度上占优点,这两个各有各的好处!

问题 浏览器如何决策使用哪一种缓存呢?

image

来自知乎 浏览器是根据什么决定「from disk cache」与「from memory cache」?

划重点!!关于 Chrome、FF、IE 的缓存区别

这个内容网络上不多有文章介绍,通过我测试以后发现区别挺大的。Chome 的缓存处理和另外两个存在明显的不一样!

上面讲的强缓存、memory cache、disk cache 在 FF、IE 中都没有明显的区分,或许这就是 Chrome 速度快的缘由?

咱们举个例子,屡次重复请求 https://www.baidu.com/,查看三个浏览器的区别。

Chrome

image

FF

image

IE

image

Chrome 中有强缓存、协商缓存,强缓存分为 memory cache、disk cache,这种处理机制能够最大化的利用缓存,减小发起的请求,从而优化页面加载速度!!Chrome 资源状态码都是 200,没有 304,另外两家都存在大量304,在 FF、IE 中,即便服务器明确表示资源须要进行强缓存,好比 Cache-Control: max-age=86400,他们仍然不会应用所谓的强缓存策略,仍然会向服务器发送请求,服务器返回 304 ,告诉浏览器用你本地的资源就行了!!!

怎么知道这个请求确实是发送到了代理或者真实服务器呢?咱们上面有说 AgeDate,Age 表示当前请求在返回时,在代理那里的时间减去 Date 的时间,因此每次只要请求发出去了,Age 都会相比上一次增长!!!

咱们以掘金的 https://b-gold-cdn.xitu.io/v3/static/js/0.81b469df7a0dd87832a4.js 文件为例。在 FF 上,前一次的结果是

image

刷一下

image

Date 没有变,表示都是使用的同一个真实服务器的响应资源,Age 后一次比前一次变大了,可是状态码都是 304,而缓存规则是 cache-control: s-maxage=2592199, max-age=2592199!!!

因此想要作好缓存,须要考虑浏览器兼容的问题,综合使用 http headers。

既然 Etag 能够校验资源是否更改,那为何还要 Last-Modified 做为备用策略

这个问题大多数讲缓存的文章也没有说起。

Etag 是经过资源内容生成的,因此会有一个计算成本存在,本如大图片的更改,它的最后更改时间能够很容易得到,可是计算 Etag 成本就会高不少了。


参考


喜欢的话,给个点赞吧~

欢迎你们关注个人掘金和公众号,算法、TypeScript、React 及其生态源码按期讲解。

相关文章
相关标签/搜索