1、缓存顺序
当依次查找缓存且都没有命中的时候,才会去请求网络。浏览器缓存机制的优先级排列如下:
Service Worker
Memory Cache
Disk Cache
Push Cache
1.1Service Worker
-
Service Worker
是运行在浏览器背后的独立线程,一般可以用来实现缓存功能。因为Service Worker
中涉及到请求拦截,所以必须使用 HTTPS 协议来保障安全。Service Worker
的缓存与浏览器其他内建的缓存机制不同,它可以让我们自由控制缓存哪些文件、如何匹配缓存、如何读取缓存,并且缓存是持续性的。 -
Service Worker
实现缓存功能一般分为三个步骤:首先需要先注册Service Worker
,然后监听到install
事件以后就可以缓存需要的文件,那么在下次用户访问的时候就可以通过拦截请求的方式查询是否存在缓存,存在缓存的话就可以直接读取缓存文件,否则就去请求数据。 -
如果我们没有在
Service Worker
命中缓存的话,会根据缓存查找优先级去查找数据。但是不管我们是从Memory Cache
中还是从网络请求中获取的数据,浏览器都会显示我们是从Service Worker
中获取的内容。
1.2.Memory Cache
Memory Cache
也就是内存中的缓存,主要包含的是当前中页面中已经抓取到的资源,如样式、脚本、图片等。关闭 Tab 页面后,缓存将失效。
1.3.Disk Cache
-
Disk Cache
也就是存储在硬盘中的缓存,读取速度慢点,但是什么都能存储到磁盘中,比之Memory Cache
胜在容量和存储时效性上。 -
在所有浏览器缓存中,
Disk Cache
覆盖面基本是最大的。它会根据HTTP Herder
中的字段判断哪些资源需要缓存,哪些资源可以不请求直接使用,哪些资源已经过期需要重新请求。并且即使在跨站点的情况下,相同地址的资源一旦被硬盘缓存下来,就不会再次去请求数据。绝大部分的缓存都来自Disk Cache
,关于HTTP
的协议头中的缓存字段,我们会在下文进行详细介绍。
1.4.Push Cache
Push Cache
(推送缓存)是HTTP/2
中的内容,当以上三种缓存都没有命中时,它才会被使用。它只在会话(Session)
中存在。
详细链接:HTTP/2 push is tougher than I thought
2、HTTP 缓存
缓存分为强缓存和协商缓存。在命中强缓存失败的情况下,才会走协商缓存。
2.1强缓存
-
2.1.1强缓存的特征
- 利用
http
头中的Expires
和Cache-Control
两个字段来控制的,若命中则直接从缓存中获取资源,不会再与服务端发生通信。
- 利用
-
2.1.2强缓存的实现方式
-
2.1.2.1 expires (http1.0)
当服务器返回响应时,会在Response Headers
中写入expires
字段。当再次请求资源,如果本地时间小于expires
时间,就会直接取缓存中的资源。expires: Wed, 11 Sep 2019 16:12:18 GMT
-
2.1.2.2 Cache-Control (http1.1)
当Cache-Control
与expires
同时出现时,以Cache-Control
为准。
详情如下:
public
:既可以被浏览器缓存,也可以被代理服务器缓存private
:该资源只能被浏览器缓存(默认值)no-cache
: 绕开了浏览器,每次请求不再询问浏览器缓存情况,而直接向服务端去确认该资源是否过期no-store
:在no-cache
的基础上,服务端的缓存也呗绕开,即直接向服务端发送请求、并下载完整的响应max-age
:表示缓存内容将在xxx秒后失效s-maxage
(单位为s):同max-age
作用一样,只在代理服务器中生效(比如CDN缓存)
-
注:s-maxage
的优先级高于max-age
。如果存在s-maxage
,则会覆盖掉max-age
和Expires header
。
2.2协商缓存
协商缓存依赖于服务端与浏览器之间的通信。
协商缓存机制下,浏览器需要向服务器去询问缓存的相关信息,进而判断是重新发起请求、下载完整的响应,还是从本地获取缓存的资源。
如果服务端提示缓存资源未改动(Not Modified),资源会被重定向到浏览器缓存(即304状态码)
-
2.2.1协商缓存的实现方式
- 如果我们启用了协商缓存,它会在首次请求时随着 Response Headers 返回:Last-Modified: Fri Sep 18 2020 10:01:04 GMT+0800
- 随后每次请求,会带上 If-Modified-Since 字段,其值正是上一次请求返回的 last-modified 值If-Modified-Since: Fri Sep 18 2020 10:01:04 GMT+0800
- 服务器会比对请求中的If-Modified-Since和服务器上的Last-Modified是否一致
如果发生了变化,就会返回一个完整的响应内容,并在 Response Headers 中添加新的 Last-Modified 值
否则,返回的 304 响应,Response Headers 不会再添加 Last-Modified 字段。
- Etag 是由服务器为每个资源生成的唯一的标识字符串,只要文件内容不同,它们对应的 Etag 就是不同的,反之亦然。因此 Etag 能够精准地感知文件的变化。
- 和Last-Modified类似:首次请求,Response Headers 返回:ETag: W/“2a3b-1602480f459”
- 再次请求,请求头中携带:If-None-Match: W/“2a3b-1602480f459”
- 服务器根据请求头中的ETag和服务器上的If-None-Matchhe否相等来进行相应的缓存策略或者返回新内容
3.HTTP 缓存决策指南
- 当我们的资源内容不可复用时,直接为 Cache-Control 设置 no-store,拒绝一切形式的缓存;
- 否则考虑是否每次都需要向服务器进行缓存有效确认,如果需要,那么设 Cache-Control 的值为 no-cache;
- 否则考虑该资源是否可以被代理服务器缓存,根据其结果决定是设置为 private 还是 public;
- 然后考虑该资源的过期时间,设置对应的 max-age 和 s-maxage 值;
- 最后,配置协商缓存需要用到的 Etag、Last-Modified 等参数。
4.缓存策略的不同触发情景
用户在浏览器如何操作时,对应触发的缓存策略主要有 3 种:
- 打开网页,地址栏输入地址: 查找 disk cache 中是否有匹配。如有则使用;如没有则发送网络请求。
- 普通刷新 (F5):因为 TAB 并没有关闭,因此 memory cache 是可用的,会被优先使用(如果匹配的话)。其次才是 disk
cache。 - 强制刷新 (Ctrl + F5):浏览器不使用缓存,因此发送的请求头部均带有 Cache-control:
no-cache(为了兼容,还带了 Pragma: no-cache),服务器直接返回 200 和最新内容。