zjffun blog

HTTP 缓存

更新于 写于 前端

当发送 GET 请求时浏览器会检查该资源的缓存信息,并决定是否发送 HTTP 请求。如果需发送请求则根据响应决定是否使用原缓存,或不使用原缓存并设置新缓存。

浏览器处理 HTTP 缓存流程(简化版)

请求阶段

  • 该资源是否有缓存?
    • 有缓存:
      • 该资源 Cache-ControlExpires 是否过期?
        • 未过期:直接使用缓存的资源。
        • 已过期:携带该资源的 ETag + If-None-MatchLast-Modified + If-Modified-Since 请求该资源。
    • 没有缓存:直接请求该资源。

响应阶段

  1. 服务端返回 304 Not Modified 则未修改使用缓存的资源
  2. 服务端返回 200 ok 则使用响应的资源,并根据响应头对改资源进行缓存

PS:在控制台查看缓存效果时注意关闭禁用 HTTP 缓存选项

缓存相关的响应头

一般来说优先级:Cache-Control > Expires > ETag > Last-Modified

Cache-Control

规定资源有效期。

Cache-Control 标头是在 HTTP/1.1 规范中定义的,取代了之前用来定义响应缓存策略的标头(例如 Expires)。所有现代浏览器都支持 Cache-Control,因此,使用它就够了。

  • Request: max-age, no-cache, no-store, only-if-cached, etc.
  • Response: max-age, no-cache, no-store, private, public, etc.

Expires

和 Cache-Control 类似,规定资源有效期。

ETag

验证令牌,在资源过期后用来检查资源是否被修改。

Hash 算法:CRC32C 或 MD5

Last-Modified

和 ETag 类似,在资源过期后用来检查资源是否被修改。

不过有些资源可能是定期生成的,最后修改时间变了但内容没变,这时使用 Last-Modified 就不合适了。

Vary

通过验证请求头和缓存的响应头 Vary 指定的项是否匹配,决定是否使用缓存。

例如,使用 Vary: User-Agent 头,缓存服务器需要通过 UA 判断是否使用缓存的页面。如果需要区分移动端和桌面端的展示内容,利用这种方式就能避免在不同的终端展示错误的布局。

定义最佳的缓存机制

可以按照下图设置Cache-Control

定义最佳的缓存机制

可缓存的内容

缓存主要包括 request method 和目标 URI(一般只有 GET 请求才会被缓存)。

普遍的缓存案例:

  • 一个检索请求的成功响应:对于 GET 请求,响应状态码为:200,则表示为成功。一个包含例如 HTML 文档,图片,或者文件的响应。
  • 永久重定向:响应状态码:301。
  • 错误响应:响应状态码:404 的一个页面。
  • 不完全的响应:响应状态码 206,只返回局部的信息。
  • 除了 GET 请求外,如果匹配到作为一个已被定义的 cache 键名的响应。

前端实现禁用缓存

方法一:文件加上版本号 /hash(推荐)

使用打包工具实现。

方法二:告诉浏览器不要缓存(不一定好使)

html
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Cache-Control" content="no-cache">
<meta http-equiv="Expires" content="-1">

方法三:请求 script 时加上随机字符串(不推荐)

html
<script>document.write('<script src="build/js/script.min.js?' + Math.rendom() + '">\x3C/script>')</script>

或者

js
var s = document.createElement('script');
s.setAttribute('src', '<script src="build/js/script.min.js?' + Math.rendom());
s.setAttribute('type', 'text/javascript');
document.getElementsByTagName('head')[0].appendChild(s);

参考