1、优点
- 缩短网页请求资源的距离,减少延迟
- 减少带宽,降低网络负荷
- 加快了客户端加载网页的速度,提升用户体验。
2、缺点
- 资源如果有更改,会导致客户端不及时更新就会造成用户获取信息滞后
3、流程
- 对于一个数据请求来说,可以分为发起网络请求,后端处理数据,浏览器响应三个步骤。浏览器缓存可以帮助我们在第一、三步中优化性能,如果浏览器中有缓存就不发起请求,发起了请求如果后端数据和前端缓存数据一致也没有必要将数据传回来,这样就减少了响应数据
4、缓存位置
- 从缓存位置上来说分为四种,根据优先级为 Service Worker、Memory Cache、Disk Cache、Push Cach,当依次查找缓存且都没有命中的时候,才会去请求网络
-
Service Worker
1. Service Worker 是运行在浏览器背后的独立线程,一般用来实现缓存功能因为 Service Worker 中涉及到请求拦截,所以必须使用 HTTPS 传输协议来保障安全 它可以让我们自由控制缓存哪些文件、如何匹配缓存、如何读取缓存,并且缓存是持续性的
-
Service Worker 实现缓存功能的三个步骤:
- 首先需要注册 Service Worker
- 然后监听 install 事件后就可以缓存需要的文件
- 最后在下次用户访问的时候,通过拦截请求的方式查询是否存在缓存,如果存在就直接读取缓存文件否则就去请求数据
当 Service Worker 没有命中缓存的时候,我们需要去调用 fetch 函数获取数据。也就是说,如果我们没有在 Service Worker 命中缓存的话,会根据缓存查找优先级去查找数据。但是不管我们是从 Memory Cache 中还是从网络请求中获取的数据,浏览器都会显示我们是从 Service Worker 中获取的内容。
Memory Cache
- 内存中的缓存:读取高效、持续时效短,会随着进程的释放而释放
- 在缓存资源时并不关心返回资源的请求头 Cache-Control 的值是什么,同时资源的匹配也并非仅仅是对 URL 做判断,还可能会对 Content-Type、Cors 等其他特征做校验
- 普通刷新 F5 操作,优先使用 Memory Cache
Disk Cache
- 硬盘中的缓存:对比 Memory Cache 读取速度慢,但是容量大、缓存时效长
- 它根据 HTTP Header 中的字段判断哪些资源需要被缓存、哪些资源可以不请求直接使用、哪些资源已经过期需要重新请求。即使在跨站点的情况下相同地址的资源一旦被硬盘缓存下来就不会再次去请求数据
- 对于大文件来说大概率在硬盘中缓存,反之内存缓存优先;当前系统内存使用率高的情况下文件优先存储进硬盘
Push Cach
- HTTP/2 中的内容,时效短只在会话 Session 中存在,一旦会话结束就会被释放
缓存过程分析 - 缓存策略
- 根据是否需要向服务器重新发起 HTTP 请求将缓存过程分为两个部分:强缓存和协商缓存
强缓存
- 不会向服务器发送请求,直接从缓存中读取资源,可以设置两种 HTTP Header 来实现:Expires、Cache-Control
Expires
- 缓存过期时间用来指定资源的过期时间,是服务器端的具体的时间点,结合 last-modified 来使用
- 是 HTTP/1 的产物,受限于本地时间,如果本地时间修改可能会造成缓存失效
Cache-Control
- 可以在请求头或响应头中来设置,多个指令配合使用达到多个目的
- 是 HTTP/1.1 的产物,如果和 Expires同时存在,那么它的优先级要高,所以 Expires 的存在成为了一种兼容性的写法
协商缓存
- 强缓存的依据来自于缓存是否过期,而不关心服务端文件是否已经更新,这可能会导致加载的文件不是服务端最新的内容,此时我们就需要用到协商缓存
- 协商缓存就是强制缓存失效后,浏览器携带缓存标识向服务器发送请求,由服务器根据缓存标识决定是否使用缓存的过程,主要分两种情况
- 协商缓存生效返回 304 和 Not Modified
- 协商缓存失效返回 200 和请求的结果
- 协商缓存可以通过设置两种 HTTP Header 来实现:Last-Modified 和 ETag
Last-Modified
浏览器在第一次返回资源时,在响应头中添加 Last-Modified 的 header,值是这个资源在服务器上的最后的修改时间浏览器接收后缓存文件和 header
浏览器下一次请求这个资源,浏览器检测到有 Last-Modified 这个 header,于是添加 If-Modified-Since 这个 header,值就是 Last-Modified 中的值;服务器再次收到这个资源请求,会根据 If-Modified-Since 中的值与服务器中这个资源的最后修改时间对比,如果没有变化,返回 304 和空的响应体,直接从缓存读取,如果 If-Modified-Since 的时间小于服务器中这个资源的最后修改时间,说明文件有更新,于是返回新的资源文件和 200
-
Last-Modified 的弊端:
- 如果本地打开缓存文件,即使没有对文件进行修改,但还是会造成 Last-Modified 被修改,服务端不能命中缓存导致发送相同的资源
- 因为 Last-Modified 只能以秒计时,如果在不可感知的时间内修改完成文件,那么服务端会认为资源还是命中了,不会返回正确的资源
既然根据文件修改时间来决定是否缓存尚有不足,能否可以直接根据文件内容是否修改来决定缓存策略?所以在 HTTP / 1.1 出现了 ETag 和If-None-Match
ETag
- ETag 是服务器响应请求时,返回当前资源文件的一个唯一标识,只要资源有变化 ETag 就会重新生成
- 浏览器在下一次加载资源向服务器发送请求时,会将上一次返回的 Etag 值放到 request header 里的 If-None-Match 里,服务器只需要比较客户端传来的 If-None-Match 跟自己服务器上该资源的 ETag 是否一致,就能很好地判断资源相对客户端而言是否被修改过了。如果服务器发现 ETag 匹配不上,那么直接以常规 GET 200 回包形式将新的资源(当然也包括了新的 ETag)发给客户端;如果 ETag 是一致的,则直接返回 304 知会客户端直接使用本地缓存即可。
- Last-Modified 和 ETag 的区别:
- 精度上 ETag 优于 Last-Modified
- 性能上 ETag 逊于 Last-Modified
- 优先级上服务器优先考虑 ETag
缓存机制
强制缓存优先于协商缓存进行,若强制缓存生效则直接使用缓存,若不生效则进行协商缓存,协商缓存由服务器决定是否使用缓存,若协商缓存失效,那么代表该请求的缓存失效,返回 200,重新返回资源和缓存标识,再存入浏览器缓存中;生效则返回 304,继续使用缓存。
如果什么缓存策略都没设置,那么浏览器会怎么处理?对于这种情况,浏览器会采用一个启发式的算法,通常会取响应头中的 Date 减去 Last-Modified 值的 10% 作为缓存时间。
-
浏览器缓存分为强缓存和协商缓存,两者有两个比较明显的区别:
- 如果浏览器命中强缓存,则不需要给服务器发请求;而协商缓存最终由服务器来决定是否使用缓存,即客户端与服务器之间存在一次通信。
在 chrome 中强缓存(虽然没有发出真实的 http 请求)的请求状态码返回是 200 (from cache); - 而协商缓存如果命中走缓存的话,请求的状态码是 304 (not modified)。 不同浏览器的策略不同,在 Fire Fox中,from cache 状态码是 304.
- 如果浏览器命中强缓存,则不需要给服务器发请求;而协商缓存最终由服务器来决定是否使用缓存,即客户端与服务器之间存在一次通信。
实际应用场景
- 频繁变动的资源:Cache-Control: no-cache
- 不常变化的资源:Cache-Control: max-age=31536000,一年的有效期,比如 jQuery 类库基本不换化
用户行为对浏览器缓存的影响
- 打开网页,地址栏输入地址: 查找 disk cache 中是否有匹配。如有则使用;如没有则发送网络请求。
- 普通刷新 (F5):因为 TAB 并没有关闭,因此 memory cache 是可用的,会被优先使用(如果匹配的话)。其次才是 disk cache。
- 强制刷新 (Ctrl + F5):浏览器不使用缓存,因此发送的请求头部均带有 Cache-control: no-cache(为了兼容,还带了 Pragma: no-cache),服务器直接返回 200 和最新内容。