缓存

面试中一个经常被问到的问题。首先缓存有很多种例如 CDN 缓存,数据库缓存,代理服务器缓存,浏览器缓存,这里主要说说浏览器缓存

什么是浏览器缓存

简单理解,主要指 http 的缓存——即协议层,应该是浏览器储存了的一些页面资源文件,例如 js,css,图片等等

浏览器缓存有哪些类别

  • 强缓存
  • 协商缓存

强缓存

只有当缓存失效,才会去服务端获取新的资源,否则都使用本地缓存。
与强缓存相关的属性

  • Expries (http/1.0)
  • Cache-Control (http/1.1)

Expires: Thu, 10 Nov 2017 08:45:11 GMT

Cache-Control: max-age=2592000

Expires 是一个绝对时间,但是如果客户端更改了时间,则会导致计算偏差,从而导致缓存失效。

Cache-Control 是一个相对时间,表示资源缓存的最大有效时间,在该时间内不需要向服务端发起请求。同时 Cache-Control 能够设置的力度比较细腻。其能够带的值有下面一些:

  • max-age:即最大有效时间,在上面的例子中我们可以看到
  • no-cache:表示没有缓存,即告诉浏览器该资源并没有设置缓存
  • s-maxage:同 max-age,但是仅用于共享缓存,如 CDN 缓存
  • public:多用户共享缓存,默认设置
  • private:不能够多用户共享,HTTP 认证之后,字段会自动转换成 private。

协商缓存

客户端会发起一次请求查询缓存是否过期,如果没有,服务端则返回 304,客户端使用本地缓存,否则服务端直接返回数据。
与协商缓存相关的属性

  • response header: Last-Modified
  • request header: If-Modified-Since
  • response header: Etag
  • request header: If-None-Match

Last-Modified: Thu, 10 Nov 2015 08:45:11 GMT

ETag: W/*5954f167-63c6

Last-Modified 是服务端返回在 response header 中的一个属性,再次请求的时候,客户端会使用 If-Modified-Since 并带上之前的 Last-Modified 的值,服务端则会将 If-Modified-Since 和 Last-Modified 对比,如果相等,则表示没有修改,返回 304,反之则表示修改了,返回 200 状态码和数据。

ETag 是储存文件的特殊标识,因为 Last-Modified 的缺陷,只能精确到秒级,所以如果在一秒内资源文件多次改变,则 Last-Modified 会失效。ETag 也是服务端返回在 response header 中的一个属性,再次请求的时候,客户端会使用 If-None-Match 并带上之前的 ETag 的值,服务端会将 If-None-Match 和 Etag 进行比较,如果相等表示没有修改,返回 304,反制表示修改了,返回 200 状态码和数据。

Last-Modified 与 ETag 是可以一起使用的,服务器会优先验证 ETag,一致的情况下,才会继续比对 Last-Modified,最后才决定是否返回 304 或者 200

下面有个表格可以直观的看到 http 中和缓存相关的属性

属性名 值/示例 类型 作用
Pragma no-cache Response 告诉浏览器忽略资源的缓存副本,每次访问都需要去服务器拉取【http1.0中存在的字段,在http1.1已被抛弃,使用Cache-Control替代,但为了做http协议的向下兼容,很多网站依旧会带上这个字段】
Expires Mon, 15 Aug 2016 03:56:47 GMT Response 启用缓存和定义缓存时间。告诉浏览器资源缓存过期时间,如果还没过该时间点则不发请求【http1.0中存在的字段,该字段所定义的缓存时间是相对服务器上的时间而言的,如果客户端上的时间跟服务器上的时间不一致(特别是用户修改了自己电脑的系统时间),那缓存时间可能就没啥意义了。在HTTP 1.1版开始,使用Cache-Control: max-age=秒替代】
Cache-Control no-cache Response 告诉浏览器忽略资源的缓存副本,强制每次请求直接发送给服务器,拉取资源,但不是“不缓存”
no-store Response 强制缓存在任何情况下都不要保留任何副本
max-age Response 指明缓存副本的有效时长,从请求时间开始到过期时间之间的秒数
public Response 任何路径的缓存者(本地缓存、代理服务器),可以无条件的缓存改资源
private Response 只针对单个用户或者实体(不同用户、窗口)缓存资源
Last-Modified Mon, 15 Aug 2016 03:56:47 GMT Response 告诉浏览器这个资源最后的修改时间。服务器将资源传递给客户端时,会将资源最后更改的时间以“Last-Modified: GMT”的形式加在实体首部上一起返回给客户端【只能精确到秒级,如果某些文件在1秒钟以内,被修改多次的话,它将不能准确标注文件的修改时间】
If-Modified-Since Mon, 15 Aug 2016 03:56:47 GMT Request 其值为上次响应头的Last-Modified值,再次向web服务器请求时带上头If-Modified-Since。web服务器收到请求后发现有头If-Modified-Since则与被请求资源的最后修改时间进行比对。若最后修改时间较新,说明资源又被改动过,则响应整片资源内容(写在响应消息包体内),包括更新Last-Modified的值,HTTP 200;若最后修改时间较旧,说明资源无新修改,则响应HTTP 304(无需包体,节省浏览),告知浏览器继续使用所保存的cache
ETag fd56273325a2114818df4f29a628226d Response 告诉浏览器当前资源在服务器的唯一标识符(生成规则又服务器决定)
If-None-Match fd56273325a2114818df4f29a628226d Request 当资源过期时(使用Cache-Control标识的max-age),发现资源具有Etage声明,则再次向web服务器请求时带上头If-None-Match(Etag的值)。web服务器收到请求后发现有头If-None-Match则与被请求资源的相应校验串进行比对,决定返回200或304

不能缓存的请求

  • HTTP信息头中包含Cache-Control:no-cache,pragma:no-cache(HTTP1.0),或Cache-Control:max-age=0等告诉浏览器不用缓存的请求
  • 需要根据Cookie,认证信息等决定输入内容的动态请求是不能被缓存的
  • 经过HTTPS安全加密的请求
  • POST请求无法被缓存
  • HTTP响应头中不包含Last-Modified/Etag,也不包含Cache-Control/Expires的请求无法被缓存