前言
HTTP
缓存不管是在面试中,还是实际开发中都是重中之重。了解HTTP
缓存不仅可以轻松自如应对面试,而且也是在实际开发中对性能优化不可或缺的手段。
客户端缓存
缓存不仅可以存在于缓存服务器内,还可以存在客户端浏览器中。通常把客户端缓存称为临时网络文件
。浏览器缓存如果有效,就不必再向服务器请求相同的资源了,可以直接从本地磁盘内读取。另外,和缓存服务器相同的一点是,当判定缓存过期后,会向源服务器确认资源的有效性。若判断浏览器缓存失效,浏览器会再次请求新的资源。
ps:对于前端开发来说,我们只需要关注客户端缓存即可
。
浏览器第一次请求资源时,必须请求所有的资源,然后根据响应的header
内容来决定,如何缓存资源。一般而言缓存可以分为强缓存和协商缓存两种。
强缓存
当客户端缓存所要请求的数据时,客户端直接从缓存服务器中获取数据。当客户端没有缓存所请求的数据时,客户端的才会从服务端获取数据。
协商缓存
又称对比缓存,客户端会先从缓存服务器中获取到一个缓存数据的标识,得到标识后请求服务端验证是否失效(新鲜),如果没有失效服务端会返回304,此时客户端直接从缓存中获取所请求的数据,如果标识失效,服务端会返回更新后的数据。
HTTP报文组成
首先,在了解HTTP缓存之前,我们有必要先了解HTTP报文的主要组成部分。
1、HTTP
请求报文是由方法、URL、HTTP版本、HTTP首部字段
等部分构成。
2、HTTP
响应报文由HTTP版本、状态码、HTTP首部
组成。
在报文众多字段中,HTTP
首部字段包含的信息最为丰富的,它是构成HTTP
报文的要素之一。跟缓存有关的属性也包含在这里面。
首部字段
首先,我们的先了解首部字段有哪些?一般可以分为通用首部字段、请求首部字段、响应请求字段、实体首部字段,由于本文谈论的是缓存相关的,所以在这里只列举了中跟缓存相关的字段名。
通用首部字段
请求报文和响应报文都会使用的首部,主要的缓存相关字段:
Cache-Control
:控制缓存的行为。
通过指定首部字段Cache-Control
的指令,就能操作缓存的工作机制,一般可以分为缓存请求指令和缓存响应指令。
1、缓存请求指令
-
no-cache
:强制向源服务器再次验证 -
no-store
:不缓存请求或者响应的任何内容 -
max-age = '秒'
:响应的最大Age
值(缓存过期时间) -
only-if-cached
:从缓存获取资源
2、缓存响应指令
-
public
:表示缓存的版本可以被代理服务器或者其他中间服务器识别。 -
private
:意味着这个文件对不同的用户是不同的。只有用户自己的浏览器能够进行缓存,公共的代理服务器不允许缓存。 -
no-cache
:强制浏览器提交一个http
请求到源服务器进行确认。http
请求没有减少,会减少一个响应体(文件内容),这种个选项类似弱缓存。 -
max-age
: 指定缓存过期的相对时间秒数,max-ag=0
或者是负值
,浏览器会在对应的缓存中把Expires
设置为1970-01-01 08:00:00
。 -
no-store
:不缓存请求或者响应的任何内容
请求首部字段(Request Header
)
从客户端向服务端发送请求报文时使用的首部,主要的缓存相关字段:
-
If-Match
:比较实体标记(ETag
) -
If-Non-Match
:比较实体标记(与If-Match
相反) -
If-Modified-Since
:比较资源更新时间 -
If-UnModified-Since
:比较资源更新时间(与If-Modified-Since
相反)
响应首部字段(Response Header
)
从服务器向客户端发送请求报文时使用的首部,主要的缓存相关字段:
-
ETag
:资源的匹配信息(数据签名)
实体首部字段
针对请求报文和响应报文的实体部分使用的首部,用于补充内容的更新时间等与实体信息有关的内容,主要的缓存相关字段:
-
Expires
:实体主体过期的日期时间 -
Last-Modified
:资源最后修改日期时间
Expires和Cache-Control
Expires
指定缓存到期GMT
的绝对时间,如果设了max-age
,max-age
就会覆盖expires
。如果expires
到期需要重新请求。
Cache-Control
:这个是http 1.1
中为了弥补 Expires
缺陷新加入的。
ps:所有的首部字段都是基于HTTP/1.1的
资源验证
验证是否能使用缓存(协商缓存策略
)主要有两种方式:
1、Last-Modified
:最后一次修改时间
配合If-Modified-Since
和If-UnModified-Since
使用
对比上次修改时间以验证资源是否需要重新修改
last-modified
是web服务器认为文件的最后修改时间,last-modified
是第一次请求文件的时候,服务器返回的一个属性。
Last-Modified: Sat, 09 Jun 2018 08:13:56 GMT
第二次请求这个文件时,浏览器把If-Modified-Since
发送给服务器,询问该时间之后文件是否被修改过。
If-Modified-Since: Sat, 09 Jun 2018 08:13:56 GMT // 跟Last-Modified的值一样
2、Etag
: 数据签名
配合If-Match
或者If-Non-Match
使用
对比资源的签名判断是否使用缓存ETag
也是首次请求的时候,服务器返回的:
ETag: "8F759D4F67D66A7244638AD249675BE2" // 长这样
If-None-Match
也是浏览器发送到服务器验证,文件是否改变的:
If-None-Match: "8F759D4F67D66A7244638AD249675BE2" // 跟ETag的值一样
而这里Etag
主要为了解决 Last-Modified
无法解决的一些问题:
1、一些文件也许会周期性的更改,但是他的内容并不改变(仅仅改变的修改时间),这个时候我们并不希望客户端认为这个文件被修改了,而重新GET;
2、某些文件修改非常频繁,比如在秒以下的时间内进行修改,(比方说1s内修改了N次),If-Modified-Since
能检查到的粒度是s级的,这种修改无法判断(或者说UNIX
记录MTIME
只能精确到秒);
3、某些服务器不能精确的得到文件的最后修改时间。
缓存状态码
-
200 OK (from cache)
是浏览器没有跟服务器确认,直接用了浏览器缓存; -
304 Not Modified
是浏览器和服务器多确认了一次缓存有效性,再用的缓存。 -
304 Not Modified
比200 OK (from cache)
慢,指的是浏览器还向服务器确认了下If-Not-Modified
,才用的缓存。 -
412 Precondition failed (预处理错误):if-Unmodified-Since
指令,如果文件被修改:则不传输,服务器返回该状态码
浏览器中的操作对缓存的影响:
强制刷新 – 当按下ctrl+F5
来刷新页面的时候, 浏览器将绕过各种缓存(本地缓存和协商缓存), 直接让服务器返回最新的资源;
普通刷新 – 当按下F5
来刷新页面的时候,浏览器将绕过本地缓蹲来发送请求到服务器, 此时, 协商缓存是有效的
回车或转向 – 当在地址栏上输入回车或者按下跳转按钮的时候, 所有缓存都生效
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。