Q: 讲一下缓存, 强缓存和协商缓存
以下都是我个人的分析和回答, 如有纰漏请多多指出
分析
缓存
因为缓存机制实际上可以有多层, 各个层次对应各自的细分场景. 为了分析需要, 我们首先将缓存简化为: 把原本需要浏览器向服务端发起网络请求才能获得的资源直接放在浏览器本地
通过使用缓存, 既可以加快浏览器请求得到响应的速度(直接在本地读取), 又可以减轻服务端的网络压力(收到的资源请求减少)
缓存能够成立, 是基于这样一条规律: 浏览器请求的资源在一定时间内可能并不会发生改变. 在请求资源并未发生改变的时间段内, 与其重复请求相同的结果, 不如直接利用上一次请求的结果
但是在 Web 场景下, 资源不会发生改变的时间到底会持续多久? 这谁也不能完全确定. 此时我们首先引入了强缓存机制
强缓存
强缓存机制可以这么理解:
-
服务端向浏览器"打包票": "接下来五分钟内我必不更新这个资源(
Cache-Control: max-age=300
), 你放心把资源放在你那儿用!" -
既然服务端这么说了, 那在这五分钟内浏览器如果有需要, 就直接使用该资源的本地版本(请求实际由本地资源响应), 就不麻烦服务端了(浏览器请求实际并不进入网络)
强缓存响应状态码 资源实际来源 场景 缓存释放 200 form memory cache
内存缓存 刚关闭网页又马上打开 浏览器关闭 200 form disk cache
硬盘缓存 先前访问过该网页 浏览器清理硬盘缓存
可五分钟之后呢? 这时就引入了协商缓存机制
协商缓存
当"五分钟"过去, 浏览器该怎么办呢? 把放在本地的资源扔了向服务端要份新的? 可要是资源没有更新版本, 那不就浪费了吗? 可要是资源更新了版本, 那肯定是用新版本更舒坦呀!
这时候与其精神内耗, 不如主动出击! 浏览器直接向服务端发问: "这资源到底改没改呀? 要是没改我就拿着本地的版本接着用了, 改了就给我整份新的呗!"
这就是协商缓存的大致逻辑: 当资源放在本地作为强缓存的时间超过服务端保证的时间, 浏览器并不会直接重新请求该资源, 而是向服务端询问资源是否更新版本. 该"询问"的过程如下:
-
浏览器发现缓存资源过旧, 向服务端询问资源是否更新版本:
依据字段 含义 对应条件请求字段 作用 Etag
资源某一版本的唯一标识(只能保证在单一主机上唯一) If-None-Match
判断该资源与服务端该资源的 Etag
是否一致Last-Modified
资源上一次被修改的时间 If-Modified-since
判断该资源从上次获取到此次请求间是否发生了更新 -
服务端向浏览器告知资源是否更新版本:
资源是否更新 响应状态码 后续处理 是 200 OK
服务端向浏览器传输该资源的最新版本 否 304 Not Modified
浏览器继续使用本地版本
回答
我们首先将缓存简化为: 把原本需要浏览器向服务端发起网络请求才能获得的资源直接放在浏览器本地. 通过使用缓存, 既可以加快浏览器请求得到响应的速度(直接在本地读取), 又可以减轻服务端的网络压力(收到的资源请求减少)
缓存能够成立, 是基于这样一条规律: "浏览器请求的资源在一定时间内可能并不会发生改变". 在请求资源并未发生改变的时间段内, 与其重复请求相同的结果, 不如直接利用上一次请求的结果
但是资源不会发生改变的时间会持续多久? 谁都无法完全确定. 因此我们首先引入了强缓存机制: 服务端在响应报文中通过
Cache-Control: max-age=[time]
字段向浏览器保证在接下来的一段时间内, 该资源一定不会被修改. 因此这一时间段内, 浏览器对该资源的请求可以直接由本地版本响应当资源作为强缓存的时间超过服务端保证的时间, 此时就引入了协商缓存机制: 浏览器将基于
Etag
字段发起条件请求If-None-Match
, 或是基于Last-Modified
字段发起条件请求If-Modified-Since
, 向服务端询问: "该资源是否发生修改?" 若发生修改, 则服务端将返回200 OK
并传输该资源的最新版本; 若未发生修改, 则服务端将返回304 Not Modified
, 告知浏览器可直接使用本地版本
个人觉得这个回答应该算比较完备了, 但其实也可以继续拓展, 比如:
- 拓展缓存: 如 CDN 和浏览器缓存等
- 深入
Etag
: 为什么需要用Etag
来区分资源版本, 而不是直接用file-v1.0.0
和file-v1.0.1
?