Skip to content

浏览器缓存机制

浏览器缓存是性能优化中最重要的环节之一。好的缓存策略可以显著减少网络传输,加快页面加载速度。

1. 缓存位置

缓存查找是有优先级的,顺序如下:

  1. Service Worker Cache:由开发者编写 JS 控制,自由度最高。
  2. Memory Cache:内存缓存。
    • 读取快,持续时间短(关闭 Tab 即失效)。
    • 通常包含图片、脚本等资源。
  3. Disk Cache:硬盘缓存。
    • 读取较慢,容量大,持久存储。
    • 通常包含 CSS 等大文件。
  4. Push Cache:HTTP/2 推送缓存(只存在于 Session 中)。
  5. 网络请求:如果没有缓存,则发起请求。

2. 缓存策略

浏览器的 HTTP 缓存策略主要分为两类:强缓存协商缓存

2.1 强缓存 (Strong Cache)

定义:浏览器直接判断本地缓存是否过期。如果未过期,直接使用,不发送请求到服务器。 状态码:200 OK (from memory cache / from disk cache)

主要通过 HTTP Header 控制:

  1. Expires (HTTP/1.0)

    • 值为服务器的绝对时间(如 Wed, 22 Oct 2026 08:41:00 GMT)。
    • 缺点:如果客户端时间被修改,缓存可能判断错误。
  2. Cache-Control (HTTP/1.1) - 优先级更高

    • max-age=3600:在 3600 秒内有效(相对时间,解决了 Expires 的问题)。
    • no-cache:不使用强缓存,必须去服务器验证(走协商缓存)。
    • no-store:禁止任何缓存(包括协商缓存),每次都下载最新资源。
    • public / private:是否允许代理服务器(CDN)缓存。

2.2 协商缓存 (Negotiation Cache)

定义:浏览器发现强缓存失效(或设置了 no-cache),则发送请求给服务器,询问资源是否更新。

  • 如果未更新:服务器返回 304 Not Modified,浏览器使用本地缓存。
  • 如果已更新:服务器返回 200 OK 和新资源。

主要通过两对 Header 控制:

  1. Last-Modified / If-Modified-Since (HTTP/1.0)

    • Response: Last-Modified (资源最后修改时间)。
    • Request: If-Modified-Since (下次请求带上上次的时间)。
    • 缺点
      • 精度只到秒级。
      • 如果文件内容没变但修改时间变了,也会重新下载。
  2. ETag / If-None-Match (HTTP/1.1) - 优先级更高

    • Response: ETag (文件内容的指纹/哈希值,如 Hash-123456)。
    • Request: If-None-Match (下次请求带上上次的 ETag)。
    • 优点:精度高,只有内容变了 ETag 才会变。
    • 缺点:服务器计算 ETag 需要耗费性能。

3. 缓存流程决策图

mermaid
graph TD
    Start[发起请求] --> SW{Service Worker 命中?}
    SW --Yes--> ReturnSW[返回 SW 缓存]
    SW --No--> Strong{强缓存有效?}
    
    Strong --Yes (Cache-Control: max-age)--> ReturnDisk[读取本地缓存 (200)]
    Strong --No (过期或 no-cache)--> Negotiate[发送请求到服务器 带上 Etag/Last-Modified]
    
    Negotiate --> ServerCheck{服务器检查}
    ServerCheck --未改变 (304)--> ReadLocal[读取本地缓存 更新 Header]
    ServerCheck --已改变 (200)--> Download[下载新资源 更新缓存]

4. 最佳实践

  1. HTML 文件

    • 使用协商缓存:Cache-Control: no-cache
    • 确保用户每次打开页面都能获取最新的 HTML(可能包含新的 CSS/JS 文件名)。
  2. CSS / JS / 图片等静态资源

    • 使用强缓存:Cache-Control: max-age=31536000 (1年)。
    • 配合 Hash 文件名(如 style.a1b2c3.css)。
    • 原理:当内容修改时,文件名发生变化(HTML 引用也随之变化),浏览器自然会请求新文件;内容不变时,直接命中强缓存。

5. Service Worker 缓存

Service Worker 是运行在浏览器后台的脚本,可以拦截网络请求,实现离线体验。

javascript
// 注册 Service Worker
navigator.serviceWorker.register('/sw.js');

// sw.js
self.addEventListener('fetch', function(event) {
  event.respondWith(
    caches.match(event.request).then(function(response) {
      // 如果命中缓存,直接返回缓存
      if (response) {
        return response;
      }
      // 否则发起网络请求
      return fetch(event.request);
    })
  );
});

Service Worker 提供了比 HTTP 缓存更细粒度的控制能力,可以实现 Cache First, Network First, Stale While Revalidate 等多种策略。

MIT Licensed | Keep Learning.