JS|浏览器缓存

1,123 阅读8分钟

浏览器缓存

why

缓存Cache一词来源于1967年的一篇论文,用于电脑工程领域,当CPU处理数据时会先到Cache中寻找,找不到的话再去内存中寻找,提供Cache的目的就是为了让数据访问的速度适应CPU的速度(更快),如今缓存Cache的概念已经被扩充,浏览器缓存也是为了解决网络加载数据的速度远远低于浏览器本身处理数据的速度。避免有些资源每次都去服务器端下载

what:

  • Web缓存是指一个Web资源(如html页面、图片、js、数据等等)存在于Web服务器和客户端(浏览器)之间的副本
  • 缓存会根据进来的请求保存输出内容的副本;当下一个请求来到的时候,如果是相同的URL,缓存会根据缓存机制决定是直接使用副本响应访问请求还是想服务器再次发送请求(也可能是访问服务器是否使用缓存)

how

缓存机制:过期机制 & 验证机制

  1. 新鲜度(过期机制):缓存副本有效期。一个缓存副本必须满足一下条件,浏览器认为它是有效的,足够新的:
    • 含有完整的过期时间控制头信息(HTTP协议报头),并且仍在有效期内
    • 浏览器已经使用过这个缓存副本,并且在一个会话中已经检查过新鲜度
  2. 校验值(验证机制):服务器返回资源的时候有时候在控制头信息带上这个资源的实体标签Etag,它可以用来作为浏览器再次请求过程的校验标识。如果发现这个校验标识不匹配,说明资源已经被修改或者过期,浏览器需要重新获取资源内容

缓存阶段:本地(强)缓存 & 协商(弱)缓存

  1. 浏览器发送请求前会先去缓存里查看是否命中强缓存,若命中,则直接从缓存中读取资源,不会发送请求到服务器,否则进入下一步
  2. 当强缓存没有命中时,浏览器一定会向服务器发送请求,服务器会根据request Header中的一些字段来判断是否命中协商缓存,如果命中,服务器会返回304响应,但是不会携带任何响应实体,只是告诉浏览器可以直接从浏览器缓存中获取这个资源,如果本地缓存和协商缓存都没有命中,则直接从服务器加载资源

本地缓存:Cache-Control、Expires

协商缓存:Last-Modified、If-Modified-Since、ETag、If-None-Match

  • HTML标签中控制

    <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
    
  • HTTP消息报头

    规则消息报头值/示例类型作用
    新鲜度ExpiresSun, 16 Oct 2021 05:00:00 GMT响应告诉浏览器在过期时间前可以使用副本
    Pragmano-cache响应告诉浏览器忽略资源的缓存副本
    Cache-Controlno-cache响应告诉浏览器忽略资源的缓存副本,强制每次请求直接发送给服务器
    no-store响应强制缓存在任何情况下都不要保留任何副本
    Max-age=[秒]响应指明缓存副本的有效时长,从请求时间开始到过期时间之间的秒数
    public响应任何途径的缓存,可以无条件的缓存该资源
    Private响应只针对单个用户或实体缓存资源
    Last-ModifiedSun, 16 Oct 2021 05:00:00 GMT响应告诉浏览器当前资源的最后修改时间
    If-Modified-SinceSun, 16 Oct 2021 05:00:00 GMT请求如果浏览器第一次请求时响应中Last-Modified非空,第二次请求同一资源时,会把它作为该项的值发送给服务器
    校验值ETag50b1c2c24c433cr:df3响应告诉浏览器当前资源在服务器的唯一标识符
    If-None-Match50b1c2c24c433cr:df3请求如果浏览器第一次请求时响应中ETag非空,第二次请求同一资源时,会把它作为该值发送给服务器
    辅助Vary响应辅助从多个缓存副本中筛选合适的版本
Expires:

HTTP1.0的特性,表示该资源过期的时间点,它是一个绝对值,这个时间点之后,缓存的资源过期。修改本地时间,可能会造成缓存失效

Expires: Wed, 1 May 2021 00:00:00 GMT
Cache-Control:

HTTP1.1提出的新特性,为了弥补Expires的缺陷而加入的,提供了更加精确细致的缓存功能,被用于在http请求和响应中,通过制定指令来实现缓存机制。

  • public:表明响应可以被任何对象缓存,即使是通常不可缓存的内容
  • private:表明响应只能被单个用户缓存,不能作为共享缓存
  • no-cache:在发布缓存副本之前,强制要求缓存把请求提交给原始服务器进行验证
  • no-store:缓存不应存储有关客户端请求或服务器响应的任何内容,即不使用任何缓存
  • max-age=:设置缓存存储的最大周期,超过这个时间被认为过期
  • s-maxage=:覆盖max-age或者Expires头,但是仅适用于共享缓存(比如各个代理),私有缓存会忽略它
  • max-stale=[]:表明客户端愿意接收一个已经过期的资源。可以设置一个可选的秒数,表示响应不能已经过时超过该给定的时间。
  • min-fresh=:表示客户端希望获取一个能在指定的秒数内保持其最新状态的响应。
  • must-revalidate:一旦资源过期(比如已经超过max-age),在成功向原始服务器验证之前,缓存不能用该资源响应后续请求。
  • proxy-revalidate:与must-revalidate作用相同,但它仅适用于共享缓存(例如代理),并被私有缓存忽略。
  • no-transform:不得对资源进行转换或转变。Content-EncodingContent-RangeContent-Type等HTTP头不能由代理修改
  • only-if-cached:表明客户端只接受已缓存的响应,并且不要向原始服务器检查是否有更新的拷贝。

优先级:Cache-Control优先级高于Exoires

Last-Modified & If-Modified-Since

Last-Modified(Response Header)与 If-Modified-Since(Request Header)是一对报文头,属于 http 1.0。

If-Modified-Since是一个请求首部字段,并且只能用在GET或者HEAD请求中,Last-Modified是一个响应首部字段,包含服务器认定的资源做出修改的日期及时间。当带着If-Modified-Since头访问服务器请求资源时,服务器会检查Last-Modified,如果Last-Modified的时间早于或等于If-Modified-Since则返回一个304,否则重新返回资源

ETag & If-None-Match

ETag是一个响应首部字段,它是根据实体内容生成的一段hash字符串,表示资源的状态,由服务器产生,If-None-Match是一个条件式的请求首部。如果请求资源时在请求首部加上这个字段,值为之前服务器返回的ETag,则当且仅当服务器上没有任何资源的ETag属性值与这个首部中冲突时,服务器才会返回带有请求资源实体的200响应,否则返回304

优先级别:ETag 优先级比 Last-Modified 高,同时存在时会以 ETag 为准

ETag解决了什么问题:

a. Last-Modified 标注的最后修改只能精确到秒级,如果某些文件在 1 秒钟以内,被修改多次的话,它将不能准确标注文件的新鲜度;

b. 某些文件也许会周期性的更改,但是他的内容并不改变(仅仅改变的修改时间),但 Last-Modified 却改变了,导致文件没法使用缓存;

c. 有可能存在服务器没有准确获取文件修改时间,或者与代理服务器时间不一致等情形。

为什么要用ETag?

  • 一些文件也许会周期性的更改,但是他的内容并不改变(仅仅改变的修改时间),这个时候我们并不希望客户端认为这个文件被修改了,而重新GET;
  • 某些文件修改非常频繁,比如在秒以下的时间内进行修改,(比方说1s内修改了N次),If-Modified-Since能检查到的粒度是s级的,这种修改无法判断(或者说UNIX记录MTIME只能精确到秒);
  • 某些服务器不能精确的得到文件的最后修改时间。

如何选择合适的缓存:

大致的顺序

  • Cache-Control —— 请求服务器之前
  • Expires —— 请求服务器之前
  • If-None-Match (Etag) —— 请求服务器
  • If-Modified-Since (Last-Modified) —— 请求服务器

协商缓存需要配合强缓存使用,如果不启用强缓存的话,协商缓存根本没有意义

大部分web服务器都默认开启协商缓存,而且是同时启用【Last-Modified,If-Modified-Since】和【ETag、If-None-Match】

但是下面的场景需要注意:

  • 分布式系统里多台机器间文件的Last-Modified必须保持一致,以免负载均衡到不同机器导致比对失败;
  • 分布式系统尽量关闭掉ETag(每台机器生成的ETag都会不一样);

Cookie

---占坑---

LocalStorage

---占坑---

SessionStorage

---占坑---

IndexDB

---占坑---

Service Worker

---占坑---

参考文章:

彻底弄懂浏览器缓存策略

浏览器缓存机制