返回 blog
2021年1月29日
2 分钟阅读

前端缓存

前端缓存不是localStorage/sessionStorage/cookie,它是浏览器根据一定的策略去缓存服务端返回的数据

图解

** | | 名称 | 概念 | 备注 | | ------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------- | ----------------------- | | expires | 主要是针对HTTP1.0,是响应头部中的一个字段,以日期的形式告诉客户端,缓存该实体资源到什么时候 | 优先级比cache-control低 | | cache-control | 指定了cache-control字段后,会覆盖expires和浏览器默认缓存机制。 | 详情见下文 | | last-Modified | 是响应头部中的一个字段,指定了服务器认为资源上一次改变的时间 | 优先级比Etag低 | | Etag | 是资源版本的标识符 | 详情见下文 |

详解

expires

http响应头中的一个字段,告诉客户端,返回的实体资源什么时候过期。 若cache-control中指定了 max-age / s-max-age ,那么expires失效 返回的示例: expires: Tue, 02 Feb 2021 11:02:47 GMT 

cache-control

是通用的消息头字段(可以在request header中,也可以在response header中)。但是缓存是单向的,请求缓存的配置只会影响请求缓存,响应缓存的配置只会影响响应缓存

请求缓存指令

客户端可以在http请求中配置的项

Cache-Control: max-age=<seconds>
Cache-Control: max-stale[=<seconds>]
Cache-Control: min-fresh=<seconds>
Cache-control: no-cache
Cache-control: no-store
Cache-control: no-transform
Cache-control: only-if-cached

响应缓存指令

服务端在http返回中配置的项

Cache-control: must-revalidate
Cache-control: no-cache
Cache-control: no-store
Cache-control: no-transform
Cache-control: public
Cache-control: private
Cache-control: proxy-revalidate
Cache-Control: max-age=<seconds>
Cache-control: s-maxage=<seconds>
可缓存性

public : 响应内容可以被任何对象缓存(客户端和代理服务器等等),即使是通常不可被缓存的内容(如未设置max-age和expires)也会被缓存

private : 响应只能被单个对象缓存,不能共享缓存,也就是客户端才能缓存响应内容,代理服务器不可

no-cache : 用Etag或last-Modified提交给服务端,判断缓存内容是否改变,若改变了,就重新请求内容,若没有改变,就使用缓存

no-store : 不缓存任何内容

到期

max-age<seconds> : 设置缓存存储的最大周期,超过这个时间缓存被认为过期

s-maxage<seconds> : 覆盖max-age或者Expires头,但是仅适用于共享缓存(比如各个代理),私有缓存会忽略它

Etag

一个标识,标识服务端的资源是否发生改变

last-Modified

一个时间标识,标识服务端资源上次改变的时间

浏览器读取缓存步骤

步骤

  1. 浏览器根据资源的http header中的cache-control或expires判断请求是否命中强缓存,若命中强缓存,就从缓存中读取文件,不发送http请求
  2. 若没有命中强缓存,还是根据http header判断是否命中协商缓存,如cache-control: no-cache。若命中协商缓存,根据Etag(优先级更高)或last-modified,向服务器发送请求,若etag或last-modified一致,浏览器从缓存中读取资源,若不一致,浏览器发送新的http请求资源
  3. 若都没有命中,则发送新的http请求资源

例子

请求一个js文件 第一次请求时,http响应中会带有一些缓存相关字段 第二次请求时,浏览器根据上一次响应的缓存策略,判断是否命中强缓存(cache-control:max-age、expires:date),若命中强缓存,则直接从缓存中读取资源文件。若没有命中强缓存,判断是否命中协商缓存,若命中协商缓存,则根据etag或last-modified,在请求中带上if-none-match 或 if-modified-since,后端接收到请求后,优先根据if-none-match、其次根据if-modified-since,判断资源是否变化,若变化了,返回新的文件,反之,返回304

流程图

强缓存

浏览器第一次向服务器请求一个资源时,服务器返回资源,并且返回了关于这个资源的缓存策略。如果这个策略是:服务器返回的资源保存在浏览器本地,下次请求资源时,直接从本地缓存中读取,不需要再请求服务器,这种策略就叫做强缓存。 强缓存一般由expires字段(响应头部字段)或cache-control:max-age(头部通用字段)来控制

expires

http1.0时出现的一种缓存协议,服务器告诉客户端资源的过期时间,若未过期,则直接在本地缓存中读取资源。由于过期时间是从服务器返回的,由于服务器跟客户端时间差,会导致资源缓存时间错乱

cache-control: max-age

http1.1 出现的缓存策略。服务端告诉客户端,资源最长可以在本地持续多久,这是一种绝对时间,解决了expires由于时间差导致的问题

协商缓存

浏览器第一次向服务端请求一个资源时,服务端返回资源,并且返回了关于这个资源的缓存策略。如果这个策略是:服务端返回了文件,且带有文件摘要或文件上次修改时间,告诉客户端,把资源放在本地,下次请求时,将文件摘要或上次修改时间,一并发送给服务端,服务端根据文件摘要或上次修改时间,判断文件是否修改,若没有修改,返回304,告诉浏览器继续使用本地缓存资源,若修改了,返回新的资源,且httpcode为200。 协商缓存一般由etag字段或last-modified字段控制

last-Modified、 If-Modified-Since

在responseHeader中,或许可以看到last-Modified字段,他的值为资源最后更新时间 ** 如果返回中带有last-Modified字段,则:

  1. 浏览器再次请求该资源时,会在requestHeader中带上 If-Modified-Since ,它的值就是上一次返回的 last-Modified 的值
  2. 服务端收到请求后,判断 If-Modified-Since 到服务端当前时间内,资源是否更新,若更新了,返回新的资源,并且带上 last-Modified 为最新的时间,若没有更新,则返回304,浏览器命中协商缓存,从本地缓存中读取资源

Etag、If-None-Match

有些情况下仅判断最后修改日期来验证资源是否有改动是不够的: 存在周期性重写某些资源,但资源实际包含的内容并无变化; 被修改的信息并不重要,如注释等; Last-Modified无法精确到毫秒,但有些资源更新频率有时会小于一秒。 为解决这些问题,http允许用户对资源打上标签(ETag)来区分两个相同路径获取的资源内容是否一致。通常会采用MD5等密码散列函数对资源编码得到标签(强验证器);或者通过版本号等方式,如W/”v1.0”(W/表示弱验证器)。 ETag为相应头部字段,表示资源内容的唯一标识,随服务器response返回

如果返回中带有Etag字段,则:

  1. 浏览器再次请求该资源时,会在RequestHeader中带上 If-None-Match ,它的值是服务端上一次返回的Etag值
  2. 服务端收到请求后,根据请求的资源,重新计算生成Etag,判断 If-None-Match 与新的Etag是否相等,若相等,则命中协商缓存,返回304。 若不相等,服务端返回新的资源和新的Etag,httpCode为200

缓存位置

浏览器把文件缓存在哪个地方?

serviceWorker

memory cache

disk cache

空间

  • 浏览器和服务端:服务端需要决定使用哪种缓存策略并在响应头返回;前端不需要设置,是浏览器本身机制。
  • html和静态资源:通常html不设置缓存,因为其它资源的入口都是html文件;静态资源(js,css,图片等)会设置缓存