它是运行在浏览器后台的独立线程,可以将它理解为是一个代理服务器。可以拦截所有的网络请求,也可以缓存数据。主要是为了创建有效的离线体验,比如后台同步,离线渲染,推送通知等。
它的缓存机制也比较特殊,可以自由的控制:缓存内容,匹配规则,读取规则,有效期等。
实现缓存的大致步骤:
install
事件提前缓存资源。fetch
事件,拦截网络请求并返回已缓存的资源。它是内存中的缓存,主要缓存当前页面中已经抓取到的资源,例如已经下载的样式、脚本、图片等,保存一份资源的引用,以备下次使用。
注意,Memory Cache 是浏览器为了加快读取缓存速度而进行的自身的优化行为,不受开发者控制,也不受 HTTP Header 的约束,算是一个黑盒。
访问页面后,再次刷新,可以看到很多数据来自内存缓存:
特点:读取速度快,但时效性很短,会随着进程结束而释放。比如关闭标签页,内存中的缓存就被释放了。
而因为计算机可使用的内存一般都比较小,操作系统对内存的使用会更精细化,所以可供浏览器使用的并不会很多。
作用:该缓存机制保证了一个页面有2个以上相同的资源请求时,实际最多只会被请求一次,如上图所示。
重点
它是存储在硬盘中的缓存,是覆盖面最大的缓存机制,会根据 HTTP Header 中的字段(Cache-Control等)来判断:
并且在跨站点的情况下,相同地址的资源一旦被硬盘缓存下来,就不会再去请求数据。一般来说,绝大部分缓存都来自 Disk Cache。
特点:读取速度较慢,但时效性很好(可设置)。
因为时效性的原因,Disk Cache 也算是持久性存储,所以也会面对容量增长的问题。
而浏览器解决这个问题,是通过特殊的算法把 “最老的” 和 “最有可能过时” 的资源一个一个的删除。
称为推送缓存或启发式缓存,是HTTP2 新增的内容。
上面3种缓存都没有命中时,它才会被使用,并且时效性很短,只在 session 会话中存在,会话结束就被释放了。
这个我了解的不多,网上也没有找到合适的文章,就不多做介绍了。
其实是对 Disk Cache 进行的分类。
当浏览器请求资源后,会优先访问强缓存。存在则返回,否则请求服务器,响应后再次写入强缓存。
作用:直接减少请求数量,是提升最大的缓存策略。通过缓存优化网页性能时是应该首先被考虑的。
实现:通过 HTTP Header 中的字段:Expires 和 Cache-control。
是 HTTP 1.0 的字段,表示缓存到期的时间,是一个绝对时间(当前时间+缓存时间)
Expires: Wed, 20 Dec 2023 23:09:07 GMT
在响应头中设置该字段,告诉浏览器在过期之前不要再次请求。
但是这个字段有明显的缺点:
对比正常的时间格式
new Date() // Wed Dec 20 2023 23:09:07 GMT+0800 (中国标准时间)
new Date('Wed Dec 20 2023 23:09:07 GMT+0800') // Wed Dec 20 2023 23:09:07 GMT+0800 (中国标准时间)
在已知 Expires 的缺点后,在 HTTP 1.1 中增加该字段,是一个相对时间,表示资源缓存的最大有效时间,该时间内不需要再次向服务器发送请求。
Cache-Control: max-age=3600
下面列举一些 Cache-control 的常用值,更多的字段参考 MDN
max-age,最大有效时间
must-revalidate(响应头信息),如果超过了 max-age 的时间,浏览器必须向服务器发送请求,验证资源是有效性。
no-cache,客户端会缓存资源,但每次都得发送请求验证有效性,等价于 max-age=0 must-revalidate
。
no-store,真正意义上的“不要缓存”。所有内容都不走缓存,包括强缓存和协商缓存。
private(默认值),所有的内容只有客户端才可以缓存,代理服务器不能缓存。
这些值可以混合使用,优先级如下:(网图,我翻译了一下)
其他一些注意点:
当强缓存超时失效后,就需要使用协商缓存,有服务器来决定缓存资源是否失效(也就是强缓存中的有效性判断。)
服务器资源为更新时,验证请求返回 304,大致流程如下图示:
而如果资源更新了,验证请求返回 200
特点:协商缓存并不会减少请求的数量。但如果是304,请求只会返回一个状态码,没有实际的资源内容,所以会减少响应体体积,来缩短网络传输时间。
协商缓存作为强缓存失效的后备解决方案,一般会和强缓存一起使用。通过2组字段来实现:
Last-Modified 是响应头,If-Modified-Since 是请求头。
Last-Modified: Thu, 21 Dec 2023 00:28:00 GMT
缺点:因为它的时间单位是秒,所以如果资源的更新速度是秒以下的单位,则无法使用该缓存。
为了解决上面的问题,所以在出现了这组新的字段。
ETag 是响应头,If-None-Match 是请求头。
ETag 是资源的标识符,一般为一个 hash 值,在服务器存储。
整体流程和 Last-Modified & If-Modified-Since 类似,只是做了替换 Last-Modified --> ETag ;If-Modified-Since --> If-None-Match。
流程如下图示:
二者对比:
Disk Cache 整体流程图:
当浏览器要请求资源时:
从 Service Worker 中获取内容(如果设置了 Service Worker)。
查看 Memory Cache。
查看 Disk Cache。细分为:
如果有强制缓存且未失效,则使用强制缓存,不请求服务器。这时的状态码全都是 200。
如果有强制缓存但已失效,使用协商缓存,比较后确定 304 还是 200。
发送网络请求,等待网络响应。
把响应内容存入 Disk Cache(如果 HTTP 响应头信息有相应配置的话)。
把响应内容的引用存入 Memory Cache(无视 HTTP 头信息的配置)。
把响应内容存入 Service Worker 的 Cache Storage(如果设置了 Service Worker)。
用户对浏览器的不同操作,会触发不同的缓存读取策略。
常见的有3种:
Cache-Control: no-cache
或(二者等效)
Cache-Control: max-age=0, must-revalidate
表示浏览器可以缓存资源,但每次使用缓存资源前都必须重新验证其有效性(通过 ETag 或者 Last-Modified)。
这样虽然每次都会发起 HTTP 请求,但当缓存内容仍然有效时可以跳过 HTTP 响应体的下载,来减少响应数据的大小。
Cache-Control: max-age=31536000
这类资源,可以设置一个很大的有效时间 31536000(一年)。这样浏览器之后请求相同 url 时会命中强制缓存。
由此带来的更新问题,可以在文件名后添加 hash,版本号等字符,从而达到更改资源 url 的目的。
注意之前的强制缓存并未失效,只是不再使用了而已。
以上。