什么是缓存
缓存是一种保存资源副本并在下次请求时直接使用副本的技术。缓存包括HTTP缓存和浏览器缓存。下文将详细讲解HTTP缓存。
缓存的作用
- 直接从本地获取缓存资源,降低了服务器压力,提升了加载速度
- 通过复用以前获取的资源,可以显著提高网站和应用程序的性能
什么是http缓存
HTTP缓存就是当浏览器第二次向服务器请求资源时,会先抵达浏览器缓存,如果浏览器有"要请求资源"的副本,就直接从浏览器缓存中提取,不再向服务端发送请求获取数据。
HTTP缓存大致分为强缓存和协商缓存 这两类。这两种缓存策略都是服务端设置 HTTP的Header 来实现的。
强制缓存如果生效,不需要再和服务器发生交互,而协商缓存不管是否生效,都需要与服务端发生交互。
HTTP服务器常见响应返回状态码304,304代表告诉浏览器本地有缓存数据,可直接从本地获取,无需再发送请求向服务器获取
强缓存
直接从浏览器缓存的本地进行获取,不会去请求服务器。
当浏览器向服务器请求数据的时候,服务器会将数据和缓存的规则返回,在响应头的 header 中,有三个字段 Expires
、Pragma
和Cache-Control
共同决定。
Expires
Expires是HTTP1.0版本的属性,它的值是一个日期,是一个绝对时间,是当前返回数据缓存到期时间戳。当浏览器在进行请求的时候,系统时间会和expires时间做对比,如果系统时间超过了expires的值,表示浏览器缓存失效,会再向服务器端请求获取资源。
问题: 由于是系统时间做对比,如果手动改变电脑系统的时间,会有缓存有效期不准的问题,这就是HTTP1.0的问题,expires的优先级在三个header属性中最低的
Pragma
Pragma是http1.0版本的属性,它只有一个属性值,为no-cache,表示不使用强缓存,每次在向客户端(浏览器)提供响应数据时,缓存都要向服务器评估缓存响应的有效性。但目前基本不使用Pragma
Pragma和Cache-Control类似,但Pragma: no-cache可以应用到HTTP 1.0 和HTTP 1.1,而Cache-Control: no-cache只能应用于HTTP 1.1
cache-control
cache-control是HTTP 1.1中新增的属性,在请求头和响应头中都可以使用,常见的属性值有
属性值 | 属性值代表意思 |
---|---|
public | 表示缓存可以被任何对象(包括发送请求的客户端、中间代理、CDN缓存等) |
private | 表示用户只能被浏览器缓存,不能作为共享缓存,即代理服务器和CDN不能缓存比响应 |
no-cache | 不使用强缓存,需要与服务器验证是否为最新资源 |
no-store | 禁止使用缓存(包括协商缓存),每次都向服务器请求最新的资源 |
max-age | 单位是秒,缓存存储的最大周期,超过这个周期被认为过期 |
must-revalidate | 当页面过期时,去服务器进行获取 |
强缓存优先级
如果三者同时存在,则 Pragma优先级>cache-control优先级 > expires优先级
使用
js
// 控制浏览器不要缓存:
// 由于历史原因,设置不读取缓存的时候三者都需要设置。
response.setDateHeader("Expires", -1);
response.setHeader("Pragma", "no-cache");
response.setHeader("Cache-control", "no-cache");
// 控制浏览器使用缓存:
response.setDateHeader("Expires", System.currentTimeMillis()+1000*60*60*24); //24小时
response.setHeader("Cache-control", "max-age=60"); //60秒(优先级更高)
协商缓存
每次请求需要让服务器判断一下资源是否更新过,从而决定浏览器是否使用缓存,如果是,则返回 304,否则重新完整响应。
协商缓存表示在使用本地的缓存之前,会先向服务器发一个请求,与服务器协商当前浏览器的缓存是否已经过期了,如果没过期,那么就使用本地的资源,如果过期了就去请求最新资源。协商缓存主要是解决强缓存资源不能及时更新的问题
协商缓存主要通过两种方式实现: Last-Modified
与IfModified-Since
,Etag
与If-None-Match
Last-Modified 与 IfModified-Since
-
Last-Modified和if-Modified-Since的值都是GMT格林尼治格式的事件字符串,代表的是文件的最后修改时间
-
在客户端第一次请求服务器时,服务器会读取这个文件的最后修改时间,然后设置Last-Modified参数到响应头,参数值就是文件最后修改的时间戳,将其返回并存储在客户端
-
客户端再次请求服务器时,请求头会带上上一次响应头的Last-Modified的时间,并将其放到if-Modified-Since请求头属性中
-
服务端收到此请求头发现有if-Modified-Since,则将这个参数与Last-Modified进行对比,如果时间一致,则服务器端的资源没有变化,自动返回HTTP状态码304(Not Changed.)状态码,内容为空,客户端接到之后,就直接把本地缓存文件显示到浏览器中
-
如果时间不一致,则服务器端资源发生改变或者重启服务器,就返回HTTP状态码200和新的文件内容,客户端接到之后,会丢弃旧文件,把新文件缓存起来,并显示到浏览器中
Etag 与 If-None-Match
-
Etag和If-None-Match的值是一串hash码,代表的是一个资源的标识符
-
Etag是服务端为文件生成的一个唯一标识,资源的每一次变化都会导致Etag的变化,即当客户端第一次请求服务器时,服务器端会在响应头塞进Etag以及其计算出的字符串参数,将其返回给客户端
-
客户端接收到Etag后,在第二次请求会带上If-None-Match的参数,再与被请求资源的唯一标识Etag进行对比,如果相同,则没有修改,响应返回304,浏览器直接从缓存中获取资源;反之,返回200,响应整个资源内容
协商缓存优先级
如果Etag和Last-Modified同时存在时,则 Etag优先级>Last-Modified优先级
强缓存和协商缓存的优先级
如果强缓存和协商缓存同时存在,如果强缓存还在生效期则强制缓存覆盖协商缓存,对比缓存不生效;如果强缓存不在有效期,协商缓存生效。则强缓存优先级 > 协商缓存优先级
总结
通过前文,我们了解到 HTTP 缓存主要分强制缓存和协商缓存。强制缓存由 Cache-Control
(HTTP1.1),Exipres
(HTTP1.0)控制。浏览器直接读本地缓存,不会再跟服务器端交互,状态码 200。协商缓存由 Last-Modified
与IfModified-Since
, Etag
与If-None-Match
实现,每次请求需要让服务器判断一下资源是否更新过,从而决定浏览器是否使用缓存,如果是,则返回 304,否则重新完整响应,返回200。