我们客户端向服务器请求资源是一件成本较高的事情,因为链路较长、网络不可控;
我们需要把这个获取到的数据缓存起来啊,下次请求时尽可能使用缓存的数据;
HTTP本身是基于【请求-应答】模式,基于这个模式,缓存可分为【服务器缓存】和【客户端缓存】;
服务器的缓存控制(Cache-Control)
当我们需要一个资源时,是这样的:
浏览器先去拿本地缓存数据,没有缓存数据就发送请求,向服务器获取资源;
服务器就响应请求,返回资源,同时标记资源的有效期;
浏览器获取到资源并缓存,等待下次请求时优先使用;
示意图
max-age
服务器中使用Cache-Control头字段来标记资源的有效期,值【max-age=30】代表这个资源只能缓存30秒,之后就过期不能使用了;
其中这个max-age所代表的时间起点时响应报文创建的时间也就是离开服务器的时刻,而不是客户端收到报文的时间,这点与Cookie中的Max-Age是有点区别的;
当我们max-age=6,其中传输过程花费4S,那么这个资源的有效期就剩2S了;
除了max-age的缓存控制字段,在头字段中还有其他三位老哥来精确【指示浏览器如何使用缓存】
no_store 不允许缓存
用于某些变化非常频繁的数据
no_cache 可以缓存
使用之前必须要去服务器验证是否过期,是否有最新的版本;
must_revalidate
如果缓存不过期就可以使用,如果过期就必须去服务器验证;
整体服务器缓存控制的流程图
客户端的缓存控制
在整个【请求-应答】模式中,对于Cache-Control字段控制缓存策略,不仅服务器可以发,浏览器也是可以发的;
刷新按钮的点击
当点击刷新按钮时,会在请求头中加入【Cache-Control: max-age=0】;
max-age=0表明我要一个最新的数据,服务器收到后也会返回一个新的报文;
Ctrl+F5强制刷新
请求报文头字段中【If-Modified-Since】和【If-None-Match】会被清空返回最新数据;
浏览器中的缓存什么时候生效?
上述我们说的两种情况,都没有使用浏览器缓存,都是向服务器要最新的数据,那么什么时候使用自己的缓存呢?
结论
浏览器中的【前进】和【后退】
条件请求
当浏览器中使用【Cache-Control】做缓存控制都是向服务器请求最新数据刷数据,不能充分体现出缓存数据的作用;并且缓存数据还会失效,使用前还必须去服务器验证是否失效;
两个连续的请求组成的【验证动作】
先发送一个HEAD请求,获取当前资源修改的时间等元信息,与缓存数据进行对比,没有变化就使用缓存,否则就发送请求获取最新的数据;
两次请求的网络成本比较高,HTTP协议定义了一系列【If】开头的【条件请求】字段,专门用来验证资源是否过期,把上述两步做的事情合并成一步;
If开头的条件请求字段
条件请求中共有5个头字段
【If-Modified-Since】和【If-None-Match】是最常用的两个
我们需要在第一次的响应报文中预先携带【Last-modified】和【ETag】,然后第二次请求时需要带上缓存里的原值就可以;
当缓存没有过期,服务器就返回一个【304 Not Modified】表示缓存依然有效,可以正常使用缓存;
Last-modified
表示资源最后修改的时间
ETag
实体标签(Entity Tag)的缩写,是资源的一个唯一标识;
主要用来解决修改时间无法准确区分文件变化的问题;
比如一个文件在1秒内修改了多次,因为修改时间是秒级的,这一秒内新版本无法区分;
比如一个文件会定期更新,但有时候会有同样的内容,但是时间在不断的变化,这是就会错误认为文件发生改变,就讲资源传输给浏览器,这样就浪费了带宽;
ETag的【强】【弱】之分
【强】ETag要求资源在字节级别上完全相同
【弱】ETag在值前有两个【W/】标记,仅仅要求资源在语义上没有变化,内部可能会有部分发生变化
比如HTML里的标签顺序的调整,或者多了几个空格;
上述我们的缓存Cache是缓存整个报文,不要错认为是body了哟!