Http 缓存
1. 强缓存
强缓存的意思就是也就是优先取本地强制缓存,当不存在本地缓存时,才去向服务器请求。
浏览器想要获取特定数据的时候,首先会检查一下本地的缓存是否存在该数据,如果存在,就直接在本地获取了,如果不存在,才会向服务器所要该数据。
想使用强缓存,那怎么判断缓存数据什么时候失效呢?
当浏览器向服务器请求数据的时候,服务器会将数据和缓存的规则返回,在响应头的 header 中,有两个字段 Expires和Cache-Control 表示当前请求的强缓存过期时间
Expires:过期时间
值为“日期/时间”,表示在此时候之后,强缓存响应过期。
无效的日期,比如 0,代表着过去的日期,即表示该资源已经过期
Expires 是Http 1.0 的产物,由于浏览器是取得本地时间与该缓存时间对比的 所以会出现,当直接修改本地时间后导致 缓存过期/应过期但被保留 的问题。 下面介绍的 Cache-Control 是 Http 1.1 增加的字段,当出现时,Expires会被忽略
Cache-Control 缓存控制
有多个可选值,且可同时配置,我们最主要关心的即 max-age(有效时间) 与 no-cache(协商缓存)还有一个基本不怎么用但很重要的 no-store(不使用任何缓存,包括协商缓存)
max-age=seconds,可设置该缓存的持续时间,我们可以认为浏览器会维护一张缓存映射表,当发起请求时,会先去缓存表中查看,如果发现其 max-age > 0 即认为当前可以使用强缓存了。
接上文 Expires,由于max-age 是一个时间长度,而不是时间戳,故此可以有效避免 Expires 修改本地时间带来的问题。
其他值:
public:响应可被任何中间节点(客户端和代理服务器)缓存。
private:只有客户端(浏览器)可以缓存,Cache-Control的默认取值。
s-maxage=< seconds >:同max-age作用一样,只在代理服务器中生效(比如CDN缓存),s-maxage优先级高于max-age,只对 public 缓存有效。设置了 s-maxage,没设置 public,代理服务器也可以缓存这个资源。
no-cache:只使用协商缓存。不使用 Cache-Control 的缓存控制方式做前置验证,而是使用 Etag 或者Last-Modified字段来控制缓存。相当于max-age:0, must-revalidate
no-store:所有内容都不会被缓存,即不使用强制缓存,也不使用协商缓存。
must-revalidate:可缓存但必须再向源服务器进确认(强缓存 + 半协商)
proxy-revalidate:要求中间缓存服务器对缓存的响应有效性再进行确认(针对public的情况)
2. 协商缓存
当强缓存不可用,且未设置 no-store 时,则进入协商缓存阶段
协商缓存有两对值,分别再 requestHeader 与 responseHeader 上,我们将分组介绍
Last-Modified 与 If-Modified-Since
顾名思义,即表示当前请求资源的“最近一次修改时间”,其匹配流程如下:
客户端请求资源 --> 服务器返回资源,并带上 Last-Modified 在 ResponseHeader 表示当前资源的最近更新时间。
客户端再次请求该资源,此时将 If-Modified-Since 放到 RequestHeader 中
服务器接收到请求,拿 If-Modified-Since 与本地文件修改时间比对,如果没有过期,则返回 304 并内容为空,告知浏览器可自行取用缓存。
如果过期,则更新新的 Last-Modified 到 ResponseHeader,并返回新的资源
但是 Last-Modified 存在一个局限性,有以下两种情况:
不该请求,还会请求。编辑了文件,文件内容没有变,但是服务器确认为我们改动了文件,所以重新设置了缓存时间,当做新请求返回给浏览器。
该请求,反而没有请求。修改文件速度很快,快过 If-Modified-Since 字段时间差(比对时会相减 < Epsilon即认为没变)的检测,文件虽然改动了,但是并没有重新生成新的资源。
由此,在 Http1.1 增加了 Etag,
Etag 与 If-None-Match
Etag 代表的意思是标识字符串。由于上述 Last-Modified 字段存在的缺陷,所以在 HTTP / 1.1 我们对资源进行内容编码,只要内容被改变,这个编码就不同。
和上述请求原理一样,浏览器首次发起请求,然后服务器在响应头返回一个标识字符串。
Etag: "62ff1a84-45ed"
浏览器再次发起请求,携带一个值相同的字符串。
If-None-Match: "62ff1a84-45ed"
服务端接收到该字符串就会作对比,如果相同,则让其读取本地缓存,否则,将新的资源返回给浏览器端。

浏览器缓存位置
1. Memory Cache 内存缓存
Memory 为内存缓存,是浏览器最先尝试命中的缓存,也是响应最快的缓存,包括当前中页面中已经抓取到的资源,例如页面上已经下载的样式、脚本、图片等。
因为存储在内存中,MemoryCache 是响应速度最快的一种缓存,但由于同样的原因,缓存持续性很短,会随着进程的释放而释放。 一旦我们关闭 Tab 页面,内存中的缓存也就被释放了。
内存缓存在缓存资源时并不关心返回资源的HTTP缓存头信息,小文件优先缓存在内存中
2. Service Worker 离线服务缓存
Service Worker 是一种独立于主线程之外的 Javascript 线程。它脱离于浏览器窗体,因此无法直接访问 DOM。
可以帮我们实现离线缓存(文件,如将index.html,js等文件缓存下来)、消息推送和网络代理等功能,缓存数据会存在浏览器的 cache storage 中。
Service Worker 实现缓存功能一般分为三个步骤:首先需要先注册 Service Worker,然后监听到 install 事件以后就可以缓存需要的文件,那么在下次用户访问的时候就可以通过拦截请求的方式查询是否存在缓存,存在缓存的话就可以直接读取缓存文件,否则就去请求数据。
使用 Service Worker的话,传输协议必须为 HTTPS。因为 Service Worker 中涉及到请求拦截,所以必须使用 HTTPS 协议来保障安全。
Service Worker 的缓存与浏览器其他内建的缓存机制不同,它可以让我们自由控制缓存哪些文件、如何匹配缓存、如何读取缓存,并且缓存是持续性的。
3. Disk Cache 磁盘缓存
Disk Cache 也就是存储在硬盘中的缓存,读取速度慢点,但是什么都能存储到磁盘中,比之 Memory Cache 胜在容量和存储时效性上。
会根据 HTTP Herder 中的字段判断哪些资源需要缓存,哪些资源可以不请求直接使用,哪些资源已经过期需要重新请求
4. Push Cache 推送缓存/启发式缓存
Push Cache(推送缓存)是针对HTTP/2标准下的推送资源设定的 ,是浏览器缓存的最后一道缓存机制,是在设置了Last-Modifed但没有设置Cache-Control或者Expires时触发,也就是只拿到最后更新时间,但没有设置过期时间,这种情况下浏览器会有一个默认的缓存策略push cache,自动设置过期时间:(Date - Last-Modified)*0.1,也就是当前时间减去最后更新时间后再乘10%。
推送缓存是session级别的,如果用户的session结束则资源被释放;即使URL相同但处于不同的session中也不会发生匹配。推送缓存的存储时间较短,Chromium浏览器中只有5分钟左右,同时它也并非严格执行HTTP头中的缓存指令
对于到底哪些文件会缓存进内存,哪些进硬盘,参考如下:
对于大文件来说,大概率是不存储在内存中的,反之优先
当前系统内存使用率高的话,文件优先存储进硬盘

可以看到:
Base64 格式的图片,几乎永远可以被塞进 memory cache,加载时间为0,都是直接加载好的。
较大的样式文件和js文件都会进 Disk Cache,加载时间也相对较长
Prefech Cache 预请求缓存
如下图,再现代工程化的前端页面中经常可以看到,prefech cache 的存在

prefetch cache 是一种浏览器预加载机制,其利用浏览器空闲时间来下载或预取用户在不久的将来可能访问的文档。网页向浏览器提供一组预取提示,并在浏览器完成当前页面的加载后开始静默地拉取指定的文档并将其存储在缓存中(即浏览器再空闲时间会优先取下载这些预加载的文件)。当用户访问其中一个预取文档时,便可以快速的从浏览器缓存中得到。
通常其用于网站性能优化,比如针对一些首屏不会直接出现,但很必要的内容(如公共提示弹窗/登录弹窗等),可设置其不在首屏直接加载代码,而是异步加载,但为了增强用户体验,如用户进来后,第一时间想要登陆,点击打开登陆弹窗,如果此时在加载,则其加载时间可能较长。 如此,我们可以为登录的相关代码设置 prefetch 预加载,提升其加载速度。
目前再Vue项目中,当设置了路由懒加载时,其就使用了prefetch,打开F12 network我们可以看到,点击其他路由后,加载的JS文件都是 from prefetch
eg:
<link ref="prefetch" href="/login.js" as="javascript">
<link rel="prefetch" href="./login.css" as="style">
PS:
- html页面缓存的设置主要是在< head >标签中嵌入< meta >标签,这种方式只对页面有效,对页面上的资源无效。
<meta http-equiv="cache-control" content="no-cache">
不常变化的资源,可使用强缓存 + hash文件名,当文件变化时,直接修改文件hash,则不会出发缓存(加载js的html文件切记不可设置强缓存)
当Expires和Cache-Control:max-age=xxx同时存在的时候取决于缓存服务器应用的HTTP版本。应用HTTP/1.1版本的服务器会优先处理max-age,忽略Expires,而应用HTTP/1.0版本的缓存服务器则会优先处理Expires而忽略max-age。


