HTTP缓存
HTTP Cache
是我们开发中接触最多的缓存, 它分为强缓存和协商缓存
- 强缓存: 直接从本地副本对比获取, 不去请求服务器, 返回的状态码是200
- 协商缓存: 会去服务器对比, 若没改变才直接读取本地缓存, 返回的状态码是304
(一)、强缓存
强缓存主要包括expires
和cache-control
1、expires
expires
是HTTP1.0
中定义的缓存字段。当我们请求一个资源, 服务器返回时, 可以在Response Headers
中增加expires
字段表示资源的到期时间
expires: Thu, 03 Jan 2019 11:43:04 GMT
它是一个时间戳(准确点应该叫格林尼治时间), 当客户端再次请求该资源的时候, 会把客户端时间与该时间戳进行对比, 如果大于该时间戳则已过期, 否则直接使用该缓存资源
但是, 有个大问题, 发送请求时是使用的客户端时间去对比。一是客户端和服务端时间可能快慢不一致, 另一方面是客户端的时间是可以自行修改的(比如浏览器是跟随系统时间的, 修改系统时间会影响到), 所以不一定满足预期
2、cache-control
正由于上面说的可能存在的问题, HTTP1.1
新增了cache-control
字段来解决该问题, 所以当cache-control
和expires
都存在时, cache-control
优先级更高。该字段是一个时间长度, 单位秒, 表示该资源过了多少秒后失效。当客户端请求资源的时候, 发现该资源还在有效时间内则使用该缓存, 它不依赖客户端时间。cache-control
主要有max-age
和s-maxage
、public
和private
、no-cache
和no-store
等值
cache-control: public, max-age=3600, s-maxage=3600
max-age
和s-maxage
两者是cache-control
的主要手段, 它们是一个数字, 表示资源过了多少秒后变为无效。在浏览器中, max-age
和s-maxage
都起作用, 而且s-maxage
的优先级高于max-age
。在代理服务器中, 只有s-maxage
起作用。可以通过设置max-age
为0表示立即过期来向服务器请求资源
2. public
和private
public
表示该资源可以被所有客户端和代理服务器缓存, 而private
表示该资源仅能客户端缓存。默认值是private
, 当设置了s-maxage
的时候表示允许代理服务器缓存, 相当于public
3. no-cache
和no-store
no-cache
表示的是不直接询问浏览器缓存情况, 而是去向服务器验证当前资源是否更新(即协商缓存)。no-store
则更狠, 完全不使用缓存策略, 不缓存请求或响应的任何内容, 直接向服务器请求更新。由于两者都不考虑缓存情况而是直接与服务器交互, 所以当no-cache
和no-store
存在时会直接忽略max-age
等
3、pragma
既然讲到了no-cache
和no-store
, 就顺便把pragma
也讲了。它的值有no-cache
和no-store
, 表示意思同cache-control
, 优先级高于cache-control
和expires
, 即三者同时出现时, 先看pragma
->cache-control
->expires
。
pragma: no-cache
(二)、协商缓存
上面的expires
和cache-control
都会访问本地缓存直接验证看是否过期, 如果没过期直接使用本地缓存, 返回200。但如果设置了no-cache
和no-store
则本地缓存会被忽略, 会去请求服务器验证资源是否更新, 如果没更新才继续使用本地缓存, 此时返回的是304, 这就是协商缓存。协商缓存主要包括last-modified
和ETag
1、last-modified
last-modified
记录资源最后修改的时间。启用后, 请求资源之后的响应头会增加一个last-modified
字段, 如下:
last-modified: Thu, 20 Dec 2018 11:36:00 GMT
当再次请求该资源时, 请求头中会带有if-modified-since
字段, 值是之前返回的last-modified
的值, 如: if-modified-since: Thu, 20 Dec 2018 11:36:00 GMT
。服务端会对比该字段和资源的最后修改时间, 若一致则证明没有被修改, 告知浏览器可直接使用缓存并返回304; 若不一致则直接返回修改后的资源, 并修改last-modified
为新的值
但last-modified
有以下两个缺点:
- 只要编辑了, 不管内容是否真的有改变, 都会以这最后修改的时间作为判断依据, 当成新资源返回, 从而导致了没必要的请求相应, 而这正是缓存本来的作用即避免没必要的请求
- 时间的精确度只能到秒, 如果在一秒内的修改是检测不到更新的, 仍会告知浏览器使用旧的缓存
2、ETag
为了解决last-modified
上述问题, 有了ETag
。ETag
会基于资源的内容编码生成一串唯一的标识字符串, 只要内容不同, 就会生成不同的ETag
。启动ETag
之后, 请求资源后的响应返回会增加一个ETag
字段, 如下:
ETag: "ALJlkaasdkljasldjLSAJDakl_"
当再次请求该资源时, 请求头会带有if-none-match
字段, 值是之前返回的ETag
值, 如: if-none-match: "ALJlkaasdkljasldjLSAJDakl_"
。服务端会根据该资源当前的内容生成对应的标识字符串和该字段进行对比, 若一致则代表未改变可直接使用本地缓存并返回304; 若不一致则返回新的资源(状态码200)并修改返回的ETag
字段为新的值。
可以看到ETag
比last-modified
更加精准地感知了变化, 所以ETag
优先级也更高。不过从上面也可以看出ETag
的存在的问题, 就是每次生成标识字符串会增加服务器的开销。所以你要如何使用last-modified
和ETag
还需要根据具体需求进行权衡
(三)、访问刷新分析
我们将访问和刷新分为以下三种情况:
- 标签进入、输入url回车进入
- 按刷新按钮、F5 刷新、网页右键“重新加载”
- ctrl + F5 强制刷新
假设当前有这么一个 index 页面,返回的响应信息如下:
cache-control: max-age=72000
expires: Tue, 20 Nov 2018 20:41:14 GMT
last-modified: Tue, 20 Nov 2018 00:41:14 GMT
1、标签进入、输入url回车进入
这种情况下会根据实际设计的缓存策略去判断。
- 由于该例没有设置
no-cache
和no-store
, 所以默认先走强缓存路线。根据cache-control
(expires
优先级低)判断缓存是否过期, 若没有过期则此时返回200(from cache)
- 若本地缓存已经过期再走协商缓存路线, 根据之前
last-modified
值去服务器对比, 若这个时间之后没有改过则去读取本地缓存, 返回304(not modified)
- 否则返回新的资源, 状态码
200(ok)
, 并更新返回响应的last-modified
值
2、按刷新按钮、F5 刷新、网页右键“重新加载”
这种情况下, 实际是浏览器将cache-control
的max-age
直接设置成了0, 让缓存立即过期, 直接走协商缓存路线。发送的请求头如下:
cache-control: max-age=0
if-modified-since: Tue, 20 Nov 2018 00:41:14 GMT
3、ctrl + F5 强制刷新
强制刷新的情况下, 浏览器会强行设置no-cache
, 强制获取最新的资源, 就连if-modified-since
等其它缓存协议字段都会被吃掉。此时发送的请求头如下:
cache-control: no-cache
pragma: no-cache
参考链接:https://www.jianshu.com/p/fb59c770160c
欢迎大家关注我的公众号,有很多关于前端的内容哦
QQ:505417246
WX:18331092918
公众号:Code程序人生
B站账号:LuckyRay123
个人博客:http://rayblog.ltd/