浏览器缓存, 是浏览器端保存数据, 用于快速读取或避免重复资源请求的优化机制, 有效的缓存使用可以避免重复的网络请求和加快页面速度, 从而提高用户体验.
一 强缓存
1.1 区分 Expires 和 Cache-Control
以一个接口返回的响应头为例:
这里我画了张思维导图, 对 Expires 和 Cache-Control 做比较:
具体介绍 Expires 和 Cache-Control:
Expires:
(1)Expires 是 HTTP1.0 的东西, 现在默认浏览器均默认使用 HTTP 1.1, 所以它的作用基本忽略;
(2)Expires 规定了缓存失效时间(Date 为当前时间), 是绝对时间. 由于 Expires 返回的是一个绝对时间, 在服务器时间与客户端时间相差较大的时候, 缓存命中不准确;
Cache-Control:
(1)Cache-Control 是 HTTP1.1 的
(2)Cache-Control 的 max-age 规定了缓存有效时间(2552s), 是相对时间;
(3)若响应头 Expires 和 Cache-Control 同时存在, Cache-Control 优先级高于 Expires
Cache-Control 的常用指令:
no-cache: 不使用本地缓存, 需要使用协商缓存, 也就是先与服务器确认缓存是否可用.
no-store: 禁用缓存. 用于防止重要的信息被无意的发布. 在请求消息中发送将使得请求和响应消息都不使用缓存.
public: 其他用户也可使用缓存, 适用于公共缓存服务器的情况.
private: 只有特定用户才能使用缓存, 适用于公共缓存服务器的情况.
不怎么常用的指令:
max-age: 客户机可以接收生存期不大于指定时间 (以秒为单位) 的响应.
min-fresh 客户机可以接收响应时间小于当前时间加上指定时间的响应.
max-stale 指示客户机可以接收超出超时期间的响应消息. 如果指定 max-stale 消息的值, 那么客户机可以接收超出超时期指定值之内的响应消息.
注意: no-cache 指令并不是不缓存, no-cache 的意思是可以缓存, 但每次用应该去向服务器验证缓存是否可用. no-store 才是不缓存内容.
1.2 强缓存的过程
强缓存: 浏览器直接从本地缓存中获取数据, 不与服务器进行交互.
. 浏览器第一次跟服务器请求一个资源, 服务器在返回这个资源的同时, 在 response 的 header 会加上 Expires/Cache-Control 的 header;
. 浏览器再请求这个资源时, 先从缓存中寻找, 找到这个资源后, 比较 Expires 或 Cache-Control 的 max-age 字段值做比较, 如果在有效期内, 则读取缓存内容; 若缓存已过期, 则重新向服务器发送请求;
. header 在重新加载的时候会被更新
这里我画了两张图, 浏览器第一次请求:
浏览器第一次请求
浏览器再次请求:
强缓存
对于强缓存, Chrome 浏览器的状态码:
200 OK(from disk cache)或是 200 OK (from memory cache)
例如: 请求某个图片后, 当浏览器再次访问这个图片时, 发现有这个图片的缓存, 且缓存没过期, 所以就使用缓存.
当浏览器发现缓存过期后, 缓存并不一定不能使用了. 比如文件虽然过了有效期, 但内容并没有发生改变, 还是可以用缓存数据. 所以, 这个时候需要与服务器协商, 让服务器判断本地缓存是否还能使用. 那么又怎么判断服务端文件有没有更新呢? 主要有两种方式:
Last-Modified,If-Modified-since.
二 协商缓存
2.1 区分 Last-Modified 和 If-Modified-Since
以一个返回的接口为例:
Last-Modified 的格式:
Last-Modified:Mon, 17 Sep 2018 12:06:18 GMT
If-Modified-Since 的格式:
If-Modified-Since:Mon, 17 Sep 2018 12:06:18 GMT
2.2 Etag 是什么
web 服务器响应请求时, 告诉浏览器当前资源在服务器的唯一标识 (生成规则由服务器决定).Apache 中, ETag 的值默认是对文件的索引节(INode), 大小(Size) 和最后修改时间 (MTime) 进行 Hash 后得到的.
2.3 协商缓存的过程
浏览器第一次请求:
浏览器第一次缓存
浏览器再一次请求:
协商缓存
Last-Modified:
. 浏览器第一次向服务器请求一个资源, 服务器在返回这个资源的同时, 在 respone 的 header 加上 Last-Modified 字段, 表示该资源在服务器上的最后修改时间;
. 浏览器再次向服务器请求这个资源时, 在 request 的 header 上加上 If-Modified-Since 字段, 这个值就是上一次请求时返回的 Last-Modified 的值;
. 服务器收到资源请求时, 比较 If-Modified-Since 字段值和被请求资源的最后修改时间, 若资源最后修改时间较旧, 则说明文件没有修改, 返回 304 Not Modified, 浏览器从缓存中加载资源; 若不相同, 说明文件被更新, 浏览器直接从服务器加载资源, 返回 200;
. 重新加载资源时更新 Last-Modified Header
If-Modified-Since
. 浏览器第一次向服务器请求一个资源, 服务器在返回这个资源的同时, 在 respone 的 header 加上 ETag 字段;
. 浏览器再次跟服务器请求这个资源时, 在 request 的 header 上加上 If-None-Match, 这个值就是上一次请求时返回的 ETag 的值;
. 服务器再次收到资源请求时, 再根据资源生成一个新的 ETag, 与浏览器传过来 If-None-Match 比较, 如果这两个值相同, 则说明资源没有变化, 返回 304 Not Modified, 浏览器从缓存中加载资源, 否则返回 200 资源内容. 与 Last-Modified 不一样的是, 当服务器返回 304 Not Modified 的响应时, 由于 ETag 重新生成过, response header 中还会把这个 ETag 返回, 即使这个 ETag 跟之前的没有变化
2.4 为什么有了 Last-Modified, 还要用 Etag 呢?
HTTP1.1 中 ETag 的出现主要是为了解决几个 Last-Modified 比较难解决的问题:
. 一些文件也许会周期性的更改, 但是他的内容并不改变(仅仅改变的修改时间), 这个时候我们并不希望客户端认为这个文件被修改了, 而重新 GET;
. 某些文件修改非常频繁, 比如在秒以下的时间内进行修改,(比方说 1s 内修改了 N 次),If-Modified-Since 能检查到的粒度是 s 级的, 这种修改无法判断(或者说 UNIX 记录 MTIME 只能精确到秒);
. 某些服务器不能精确的得到文件的最后修改时间.
对于上述情景, 利用 ETag 能够更加准确的控制缓存, 因为 ETag 是服务器自动生成的资源在服务器端的唯一标识符, 资源每次变动, 都会生成新的 ETag 值. Last-Modified 与 ETag 是可以一起使用的, 但服务器会优先验证 ETag.
2.5 比较强缓存和协商缓存
基于上文对强缓存和协商缓存过程的解释, 这里我把强缓存和协商缓存绘制在一张图里, 方便比较, 具体过程可以参照上文:
http 缓存
三 小结
本文主要通过图解介绍了 http 的缓存, 具体包括强缓存和协商缓存. 如有问题, 欢迎指正.
来源: https://www.qcloud.com/developer/article/1346293