协商缓存与强缓存
有多处转载 https://www.jianshu.com/p/fb59c770160c 等
-
缓存的优点:
- 减少了不必要的数据传输,节省带宽
- 减少服务器的负担,提升网站性能
- 加快了客户端加载网页的速度
- 用户体验友好
-
缺点
- 可能会留下bug
强缓存
- 当浏览器去请求某个文件的时候,服务端就在respone header里面对该文件做了缓存配置。缓存的时间、缓存类型都由服务端控制
- respone header 的cache-control,常见的设置是max-age public private no-cache no-store等
- 强缓存主要包括
expires
和cache-control
。
强缓存配置介绍
-
expires
expires
是HTTP1.0
中定义的缓存字段。当我们请求一个资源,服务器返回时,可以在Response Headers
中增加expires
字段表示资源的过期时间。expires: Thu, 03 Jan 2019 11:43:04 GMT
- 它是一个时间戳(准确点应该叫格林尼治时间),当客户端再次请求该资源的时候,会把客户端时间与该时间戳进行对比,如果大于该时间戳则已过期,否则直接使用该缓存资源。
- 有个大问题,发送请求时是使用的客户端时间去对比。一是客户端和服务端时间可能快慢不一致,另一方面是客户端的时间是可以自行修改的(比如浏览器是跟随系统时间的,修改系统时间会影响到),所以不一定满足预期。
-
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 表示立马过期来向服务器请求资源。 -
public
和private
public
表示该资源可以被所有客户端和代理服务器缓存,而private
表示该资源仅能客户端缓存。默认值是
private
,当设置了s-maxage
的时候表示允许代理服务器缓存,相当于public
。 -
no-cache
和no-store
no-cache
表示的是不直接询问浏览器缓存情况,而是去向服务器验证当前资源是否更新(即协商缓存)。no-store
则更狠,完全不使用缓存策略,不缓存请求或响应的任何内容,直接向服务器请求最新。由于两者都不考虑缓存情况而是直接与服务器交互,所以当no-cache
和no-store
存在时会直接忽略max-age
等。
强缓存总结
- cache-control: max-age=xxxx,public
客户端和代理服务器都可以缓存该资源;
客户端在xxx秒的有效期内,如果有请求该资源的需求的话就直接读取缓存,statu code:200 ,如果用户做了刷新操作,就向服务器发起http请求 - cache-control: max-age=xxxx,private
只让客户端可以缓存该资源;代理服务器不缓存
客户端在xxx秒内直接读取缓存,statu code:200 - cache-control: max-age=xxxx,immutable
客户端在xxx秒的有效期内,如果有请求该资源的需求的话就直接读取缓存,statu code:200 ,即使用户做了刷新操作,也不向服务器发起http请求 - cache-control: no-cache
跳过设置强缓存,但是不妨碍设置协商缓存;一般如果你做了强缓存,只有在强缓存失效了才走协商缓存的,设置了no-cache就不会走强缓存了,每次请求都回询问服务端。 - cache-control: no-store
不缓存,这个会让客户端、服务器都不缓存,也就没有所谓的强缓存、协商缓存了。
协商缓存
介绍协商缓存
- 上面说到的强缓存就是给资源设置个过期时间,客户端每次请求资源时都会看是否过期;只有在过期才会去询问服务器。所以,强缓存就是为了给客户端自给自足用的。而当某天,客户端请求该资源时发现其过期了,这是就会去请求服务器了,而这时候去请求服务器的这过程就可以设置协商缓存。这时候,协商缓存就是需要客户端和服务器两端进行交互的。
怎么设置协商缓存?
-
response header里面的设置
etag: '5c20abbd-e2e8' last-modified: Mon, 24 Dec 2018 09:49:49 GMT
- etag:每个文件有一个,改动文件了就变了,就是个文件hash,每个文件唯一,就像用webpack打包的时候,每个资源都会有这个东西,如: app.js打包后变为 app.c20abbde.js,加个唯一hash,也是为了解决缓存问题。
- last-modified:文件的修改时间,精确到秒
-
也就是说,每次请求返回来 response header 中的 etag和 last-modified,在下次请求时在 request header 就把这两个带上,服务端把你带过来的标识进行对比,然后判断资源是否更改了,如果更改就直接返回新的资源,和更新对应的response header的标识etag、last-modified。如果资源没有变,那就不变etag、last-modified,这时候对客户端来说,每次请求都是要进行协商缓存了,即:
发请求–>看资源是否过期–>过期–>请求服务器–>服务器对比资源是否真的过期–>没过期–>返回304状态码–>客户端用缓存的老资源。
-
当服务端发现资源真的过期的时候,会走如下流程:
发请求–>看资源是否过期–>过期–>请求服务器–>服务器对比资源是否真的过期–>过期–>返回200状态码–>客户端如第一次接收该资源一样,记下它的cache-control中的max-age、etag、last-modified等。
协商混存总结
-
请求资源时,把用户本地该资源的 etag 同时带到服务端,服务端和最新资源做对比。
如果资源没更改,返回304,浏览器读取本地缓存。
如果资源有更改,返回200,返回最新的资源。 -
response header中的etag、last-modified在客户端重新向服务端发起请求时,会在request header中换个key名:
// response header etag: '5c20abbd-e2e8' last-modified: Mon, 24 Dec 2018 09:49:49 GMT // request header 变为 if-none-matched: '5c20abbd-e2e8' if-modified-since: Mon, 24 Dec 2018 09:49:49 GMT
为什么要有etag?
- 你可能会觉得使用last-modified已经足以让浏览器知道本地的缓存副本是否足够新,为什么还需要etag呢?HTTP1.1中etag的出现(也就是说,etag是新增的,为了解决之前只有If-Modified的缺点)主要是为了解决几个last-modified比较难解决的问题:
- 一些文件也许会周期性的更改,但是他的内容并不改变(仅仅改变的修改时间),这个时候我们并不希望客户端认为这个文件被修改了,而重新get;
- 某些文件修改非常频繁,比如在秒以下的时间内进行修改,(比方说1s内修改了N次),if-modified-since能检查到的粒度是秒级的,这种修改无法判断(或者说UNIX记录MTIME只能精确到秒);
- 某些服务器不能精确的得到文件的最后修改时间。
怎么设置强缓存与协商缓存
- 后端服务器如nodejs:
- res.setHeader(‘max-age’: ‘3600 public’)
- res.setHeader(etag: ‘5c20abbd-e2e8’)
- res.setHeader(‘last-modified’: Mon, 24 Dec 2018 09:49:49 GMT)