# 浏览器缓存

# 简要介绍

浏览器缓存存在于服务器和客户端之间。浏览器缓存密切注视着服务器-客户端之间的通信,监控请求,并且把请求输出的内容(例如html页面、 图片和文件)另存一份;然后,如果下一个请求是相同的URL,则直接使用保存的副本,而不是再次请求源服务器。

浏览器缓存.png

# 浏览器缓存的优点

  • 减少网络延迟,提升页面加载速度
  • 降低服务器压力
  • 减少网络带宽损耗

# 浏览器缓存的缺点

  • 缓存没有清理机制
  • 给开发带来的困扰

# 缓存的过程

浏览器每次发起请求,都会先在浏览器缓存中查找该请求的结果以及缓存标识。浏览器每次拿到返回的请求结果都会将该结果和缓存标识存入浏览器缓存中。具体看下图。

162db6359673e7d0.png

根据是否需要向服务器重新发起HTTP请求将缓存过程分为两个部分,分别是强缓存和协商缓存。

# 强缓存

HTTP/1.0HTTP/1.1 当中,这个字段是不一样的。在早期,也就是 HTTP/1.0 时期,使用的是Expires,而 HTTP/1.1 使用的是 Cache-Control

# Expires

Expires 即过期时间,存在于服务端返回的响应头中,告诉浏览器在这个过期时间之前可以直接从缓存里面获取数据,不需要再次请求。

Expires: Wed, 22 May 2020 07:11:00 GMT

当首部字段 Cache-Control 有指定 max-age 指令 时,则优先处理 max-age 指令

但是有个潜在问题,服务器时间和浏览器的时间不一致,导致出现问题。在 HTTP/1.1 中已经被抛弃。

# Cache-Control

用于请求以及响应时。可添加多个指令。 多个指令使用 “,” 分隔。

  • 缓存请求指令
指令 参数 说明
no-cache 强制向服务器再次验证
no-store 不缓存请求或响应的任何内容
max-age = [秒] 必需 响应的最大Age值
max-stale( = [秒]) 可省略 接收已过期的响应
min-fresh = [秒] 必需 期望在指定时间内的响应仍有效
no-transform 代理不可更改媒体类型
only-if-cached 从缓存中获取资源
cached-extension - 新指令标记(token)
  • 缓存响应指令
指令 参数 说明
public 可向任意方向提供响应的缓存
private 可省略 仅向特定用于返回响应
no-cache 可省略 缓存前必须先确认其有效性
no-store 不缓存请求或响应的任何内容
no-transform 代理不可更改媒体类型
must-revalidate 可缓存但必须再向源服务器进行确认
proxy-revalidate 要求中间缓存服务器对缓存的响应有效性再进行确认
max-age = [秒] 必需 响应的最大Age值
s-maxage = [秒] 必需 公共缓存服务器响应的最大Age值
cached-extension - 新指令标记(token)

比如:

// 响应返回后在 3600 秒, 直接使用缓存
Cache-Control:max-age=3600

// 其他用户也可以利用缓存
Cache-Control: pulic 

当强缓存失效的话就进行下一步协商缓存。

# 协商缓存

强缓存失效之后,浏览器在请求头中携带相应的缓存tag来向服务器发请求,由服务器根据这个tag,来决定是否使用缓存,这就是协商缓存。

具体来说,这样的缓存tag分为两种: Last-ModifiedETag 。这两者各有优劣,并不存在谁对谁有绝对的优势,跟上面强缓存的两个 tag 不一样。

# Last-Modified

即最后修改时间。在浏览器第一次给服务器发送请求后,服务器会在响应头中加上这个字段。

浏览器接收到后,如果再次请求,会在请求头中携带 If-Modified-Since 字段,这个字段的值也就是服务器传来的最后修改时间。

服务器拿到请求头中的 If-Modified-Since 的字段后,其实会和这个服务器中该资源的最后修改时间对比:

如果请求头中的这个值小于最后修改时间,说明是时候更新了。返回新的资源,跟常规的HTTP请求响应的流程一样。 否则返回304,告诉浏览器直接用缓存。

# ETag

ETag 是服务器根据当前文件的内容,给文件生成的唯一标识,只要里面的内容有改动,这个值就会变。服务器通过响应头把这个值给浏览器。

浏览器接收到 ETag 的值,会在下次请求时,将这个值作为 If-None-Match 这个字段的内容,并放到请求头中,然后发给服务器。

服务器接收到 If-None-Match 后,会跟服务器上该资源的 ETag 进行比对:

如果两者不一样,说明要更新了。返回新的资源,跟常规的 HTTP 请求响应的流程一样。 否则返回304,告诉浏览器直接用缓存。

# 两者对比

在精准度上,ETag 优于 Last-Modified 。优于 ETag 是按照内容给资源上标识,因此能准确感知资源的变化。而 Last-Modified 就不一样了,它在一些特殊的情况并不能准确感知资源变化,主要有两种情况: 编辑了资源文件,但是文件内容并没有更改,这样也会造成缓存失效。

Last-Modified 能够感知的单位时间是秒,如果文件在 1 秒内改变了多次,那么这时候的 Last-Modified 并没有体现出修改了。

在性能上,Last-Modified 优于 ETag,也很简单理解,Last-Modified 仅仅只是记录一个时间点,而 Etag 需要根据文件的具体内容生成哈希值。 另外,如果两种方式都支持的话,服务器会优先考虑 ETag

# 缓存位置

当强缓存命中或者协商缓存中服务器返回304的时候,我们直接从缓存中获取资源。那这些资源究竟缓存在什么位置呢?

浏览器中的缓存位置一共有四种,按优先级从高到低排列分别是:

  • Service Worker
  • Memory Cache
  • Disk Cache
  • Push Cache

# Service Worker

Service Worker 借鉴了 Web Worker 的 思路,即让 JS 运行在主线程之外,由于它脱离了浏览器的窗体,因此无法直接访问 DOM 。虽然如此,比如离线缓存、消息推送和网络代理等功能。其中的离线缓存就是 Service Worker Cache

Service Worker 同时也是 PWA 的重要实现机制。

# Memory Cache 和 Disk Cache

Memory Cache指的是内存缓存,从效率上讲它是最快的。但是从存活时间来讲又是最短的,当渲染进程结束后,内存缓存也就不存在了。

微信截图_20200510205356.png

Disk Cache 就是存储在磁盘中的缓存,从存取效率上讲是比内存缓存慢的,但是他的优势在于存储容量和存储时长。

好,现在问题来了,既然两者各有优劣,那浏览器如何决定将资源放进内存还是硬盘呢?

主要策略如下:

  1. 比较大的JS、CSS文件会直接被丢进磁盘,反之丢进内存
  2. 内存使用率比较高的时候,文件优先进入磁盘

# Push Cache

即推送缓存,是 HTTP/2 中的内容,目前应用的还不是很广泛,就不介绍了。

Last Updated: 5/11/2020, 3:49:34 AM