Open
Description
前言
现代浏览器里,我们打开一个网页,都会发起 http 请求,浏览器请求下载完资源后,会对数据进行缓存,以保证更快的响应速度。浏览器存放缓存有以下几个地方:
Service Worker
一个独立线程,可以用做离线缓存Memory Cache
内存缓存Disk Cache
磁盘缓存Push Cache
推送缓存,与 HTTP2 有关
缓存分类
分为 强缓存
和 协商缓存
强缓存
有两个属性:Expires
和 Cache-Control
。前者是 http1.0 的产物,表示资源的过期时间,它是个绝对时间;后者是 http1.1 新增的更全的属性控制,是个相对时间。优先级 Cache-Control > Expires。
Cache-Control 有如下属性:
- max-age: 缓存会在多少秒后过期
- s-maxage: 同样表示缓存会在多少秒后过期,适用于代理服务器,它的优先级高于 max-age
- public: 表示该资源可以被任意节点缓存(客户端,代理服务器等等)
- private: 表示该资源不会被代理服务器缓存
- no-cache: 客户端可以缓存资源,但每次使用前,必须向服务器确认其有效性
- no-store: 顾名思义,不进行缓存
协商缓存
相关属性:Last-Modified
和 ETag
。
Last-Modified 表示文件的最后修改时间,每次请求时,把它的值作为 If-Modified-Since 发送给服务器,服务器会验证在该时间后资源是否有更新,有就返回新的资源并更新 Last-Modified;没有就返回 304。
ETag 类似文件指纹,每次请求会将 ETag 作为 If-None-Match 发送给服务器,服务器就会验证这个资源对应的 ETag 是否有变动,有就返回新资源;没有就返回 304。
由于 Last-Modified 感知时间粒度有限,例如当时间是毫秒级变化时,修改了文件但它也感知不到;或者是当你只是点了编辑,实际文件内容没有什么变化,也会触发时间变化,所以它有缺陷。而 ETag 是对文件内容生成哈希值,它能够有效地表示资源变化,只要内容变化,ETag 就会变,因此当 Last-Modified 和 ETag 一同出现时,服务器会优先使用 ETag。
启发式缓存
如果请求头啥都没设置怎么办?浏览器默认会采用一种启发式缓存的算法。具体就是用响应头的 Date 减去 Last-Modified 值的 10% 作为缓存时间。
缓存过程
- 初次发起请求,服务器响应 200 状态码,下载拿到资源后,对 response 进行缓存;
- 当再次加载该资源时,浏览器优先判断强缓存,比较当前时间与上次返回 200 的时间差,是否超过了 cache-control 设置的 max-age。如果没有超过,命中强缓存就不用去发请求了,直接读取缓存结果返回;如果超过了,那就发起请求,这时请求头中会带上 If-None-Match 和 If-Modifed-Since 的标识
- 服务器收到了浏览器请求后,开始协商缓存。
3.1 优先根据 Etag 的值判断文件资源是否被修改,如果没有,则命中协商缓存,返回 304;如果被修改了,那就生成新的 Etag 值,然后返回新的资源文件,状态码是 200
3.2 如果没有 Etag,那就看 If-Modifed-Since ,跟资源文件的最后修改时间做比对,如果相同,命中协商缓存返回 304;否则更新 last-modifed 并返回新的资源文件,状态码是 200