基于本文回答

播面 播面

刷题像听歌,多听自然懂
0
评论

浏览器的缓存机制(强缓存 vs 协商缓存)

知识点图片

浏览器的缓存机制是前端性能优化的核心内容之一。简单来说,浏览器缓存就是把一个已经请求过的资源(如 HTML、CSS、JS、图片)拷贝一份副本存储在本地,当下次需要该资源时,根据特定规则决定是直接使用副本,还是向服务器重新请求。

浏览器的缓存机制主要分为两类:强缓存(Strong Cache)协商缓存(Negotiated Cache)


一、 核心流程图解

在深入细节之前,先建立一个整体的决策流程:

  1. 浏览器发起请求
  2. 检查强缓存
    • 命中?直接使用本地缓存(状态码 200,显示 from memory cachefrom disk cache)。
    • 未命中(或已过期)?进入下一步。
  3. 检查协商缓存(向服务器发送请求):
    • 服务器对比后发现资源未变?返回 304 Not Modified,浏览器读取本地缓存。
    • 服务器对比后发现资源变了?返回 200 OK 和新的资源内容,浏览器更新本地缓存。

二、 强缓存 (Strong Cache)

核心概念:浏览器直接判断本地缓存是否过期。如果没过期,完全不跟服务器通信,直接使用本地文件。

控制字段(HTTP Header):
强缓存主要由 ExpiresCache-Control 两个字段控制。

1. Expires (HTTP/1.0)

  • :一个绝对的服务器时间(例如:Wed, 22 Oct 2025 08:41:00 GMT)。
  • 原理:浏览器拿当前系统时间跟这个时间对比,如果当前时间小于 Expires,则缓存有效。
  • 缺点:如果客户端时间被修改,或者客户端与服务器时间不一致,会导致判断错误。现在基本已被 Cache-Control 取代。

2. Cache-Control (HTTP/1.1) - 优先级更高

  • :相对时间或其他指令。
  • 常见指令
    • max-age=3600:表示资源在 3600 秒(1小时)内是有效的。这是最常用的。
    • no-cache注意! 这不代表不缓存,而是代表跳过强缓存,直接进入协商缓存阶段(每次都要向服务器确认)。
    • no-store真正的“不缓存”。任何地方都不存副本,每次都从服务器拿最新的。
    • public:客户端和代理服务器(CDN)都可以缓存。
    • private:只有客户端可以缓存(默认值)。

三、 协商缓存 (Negotiated Cache)

核心概念:强缓存失效(过期)或者被设置为 no-cache 后,浏览器携带缓存标识向服务器发起请求,由服务器来决定缓存是否可用。

控制字段(成对出现):
协商缓存依赖于两组字段,一组是基于时间的,一组是基于内容的。

1. Last-Modified / If-Modified-Since (基于时间)

  • Last-Modified (响应头):服务器告诉浏览器,这个资源最后修改的时间。
  • If-Modified-Since (请求头):当浏览器再次请求该资源时,会把上次收到的 Last-Modified 的值放在这里发给服务器。
  • 服务器逻辑:对比 If-Modified-Since 和服务器上文件的最后修改时间。
    • 如果一致:返回 304(资源没变,用缓存)。
    • 如果不一致:返回 200 + 新资源 + 新的 Last-Modified
  • 缺点
    • 精度问题:只能精确到秒。如果 1 秒内修改了多次,它感知不到。
    • 内容未变时间变:有时候文件只是被“摸”了一下(保存了一下),内容没变但时间变了,会导致缓存失效。

2. ETag / If-None-Match (基于内容摘要) - 优先级更高

  • ETag (响应头):服务器根据文件内容生成的唯一标识(类似哈希值/指纹)。
  • If-None-Match (请求头):浏览器再次请求时,把上次收到的 ETag 发给服务器。
  • 服务器逻辑:对比 If-None-Match 和当前文件的 ETag
    • 一致:返回 304
    • 不一致:返回 200 + 新资源 + 新的 ETag
  • 优点:非常精准,只要内容变了 ETag 就会变。
  • 缺点:服务器计算 ETag 需要消耗一定的性能。

四、 总结与对比

特性 强缓存 协商缓存
是否向服务器发请求 (直接由浏览器拦截) (发请求询问)
HTTP 状态码 200 (from cache) 304 (Not Modified) 或 200
主要 Header Cache-Control (max-age), Expires ETag, Last-Modified
谁来决定 浏览器根据 Header 自动决定 服务器对比后决定
优先级 最高 强缓存失效后才触发

Header 优先级排序:

  1. Cache-Control > Expires
  2. ETag > Last-Modified

五、 最佳实践策略

在现代前端工程化(如 Webpack/Vite 打包)中,通常采用以下缓存策略:

  1. HTML 文件

    • 使用 协商缓存 (Cache-Control: no-cache)。
    • 原因:HTML 是入口,必须保证用户拿到的是最新的引用。如果 HTML 被强缓存锁死,用户就永远拿不到更新后的 CSS/JS 文件名。
  2. 静态资源 (CSS/JS/图片)

    • 使用 强缓存 (Cache-Control: max-age=31536000),设置一个超长的过期时间(如一年)。
    • 配合 Hash 文件名:利用构建工具给文件名加上 Hash(如 main.a1b2c3.js)。
    • 原因:一旦内容修改,Hash 变化,文件名就变了,HTML 会引用新的 URL,自然就请求新文件了;如果内容没变,文件名不变,直接利用强缓存,速度极快。

六、 用户行为对缓存的影响

  • 地址栏回车 / 点击链接 / 前进后退
    • 强缓存有效,协商缓存有效。
  • F5 刷新 (普通刷新)
    • 强缓存失效 (浏览器会把 max-age 设为 0 或加上 no-cache)。
    • 协商缓存有效 (会带上 If-Modified-Since / If-None-Match 询问服务器)。
  • Ctrl + F5 (强制刷新)
    • 所有缓存失效。浏览器不带任何缓存标识,直接请求最新资源 (Header 中会带 Cache-Control: no-cachePragma: no-cache)。
00:00
00:00