http 缓存

缓存是优化网页性能的种手段,通过尽量不发起HTTP 请求(利用缓存)。来加快页面的渲染。

强缓存

http1.0 的 expires

http1.0 中,通过头部字段 expires 定义缓存有效时间。expires 是一个绝对的日期。那么这个绝对值是取决于服务器,如果服务器和客户端的时间并不一致,那么就可能导致缓存过早失效或客户端使用已经过期的缓存。

http1.1 的 Cache-Control

为了解决这个问题,http1.1 引入了 Cache-Control 字段,来设置过期时间为相对值。可以通过 max-age(单位为秒) 指定资源具体的缓存时间。

Cache-Control 的值

请求头

  • no-cache: 使用本地缓存必须向服务器验证
  • no-store: 不缓存该请求的任何内容
  • max-age: 响应的最大有效时间
  • max-stale: 接受过期多久的缓存
  • min-fresh: 期望在指定时间内的响应仍然有效
  • no-transform: 代理不可更改媒体类型
  • only-if-cached: 从缓存获取资源
  • cache-extension:新指令的标记

响应头

  • public: 任何机器都可以缓存。
  • private: 只允许特定的人缓存。
  • no-cache:要求客户端使用缓存时,必须向服务器确认。
  • no-store:不允许任何缓存
  • no-transform:代理不能更改媒体类型
  • must-revalidate:可以缓存但是必须向服务器确认
  • proxy-revalidate:中间代理必须对响应缓存的有效性再次确认
  • max-age:最大缓存有效时间
  • s-maxage:中间服务器最大缓存时间
  • cache-extension:新指令标记

强缓存的实现过程

当浏览器向服务器发起请求,服务器会将返回数据的响应头的 Cache-control:max-age=X。表示X秒后再次发起请求,可以直接使用本次的内容。

浏览器拿到响应后,会将 请求与响应 做一个缓存映射,当再次发起相同的请求是,浏览器会判断当前请求是否过期。如果,没有过期则直接使用缓存数据,状态码 200(from cache).

当然如果存在 Expires 字段,且没有Cache-control字段,那么就会拿这个字段来判断缓存是否过期。

协商缓存

强缓存过期之后才会进入协商缓存阶段。

强缓存过期后,进入了协商缓存阶段。这个阶段目的在于,判断本地缓存是否还有效。如果有效则服务器会通知浏览器使用本地缓存。

通常,我们实现协商缓存有两种方式。

Last-Modified / IF-Modified-Since

  • 通过 Last-modified/if-Modified-Since 实现
  • 当服务响应请求时,会将 Last-Modified 设置在响应头中,浏览器会记录该字段。
  • 当该文件过期后,浏览器会将第一次收到的 Last-Modified 的值,设置到 请求头的 If-Modified-Since 字段中。
  • 服务器收到请求,取出 If-Modified-Since 字段,与文件修改时间对比,如果没有改变就 返回 304(not modified),并更新浏览器缓存资源有效期。如果,不相同则返回该请求的资源。
  • 缺点:只能判断秒级的改变,当变化文件在1秒以内,浏览器会认为缓存命中返回 304。为了解决这个问题,HTTP1.1 引入 ETag

Etag / IF-None-Match

  • ETag 是通过 hash 算法,计算出来的文件唯一标识。当文件改变其计算出来的hash值也将是不同的。
  • 当浏览器第一请求资源时,服务器会计算出文件的 hash 值,并将得到值 设置到响应头的 ETag 上。
  • 浏览器接收响应,保存 ETag。当浏览缓存过期时,就将保存的 ETag 值,设置在 请求头的 If-None-Match 上。
  • 服务器接收该请求,取出If-None-Match 值,并和服务器保存的文件进行 hash 比对。如果一样则 返回 304,并更新过期时间。不一样则返回新的文件。
  • 缺点
    • hash 值的计算需要服务器的处理,如果用户量巨大。通过 ETag 进行协商缓存,对性能的浪费是巨大的。ETag 的出现并不是要代替 Last-Modified 的。