Skip to content

浏览器缓存策略

一、缓存概览

1.1 缓存位置 (按优先级)

1. Service Worker Cache
2. Memory Cache (内存缓存)
3. Disk Cache (硬盘缓存)
4. Push Cache (HTTP/2)
5. 网络请求
缓存类型特点
Memory Cache速度快,容量小,关闭 Tab 即失效
Disk Cache速度较慢,容量大,可持久化
Service Worker可编程控制,离线可用

1.2 缓存策略分类

HTTP 缓存
├── 强缓存 (不发请求)
│   ├── Expires
│   └── Cache-Control
└── 协商缓存 (发请求验证)
    ├── Last-Modified / If-Modified-Since
    └── ETag / If-None-Match

二、强缓存

2.1 Expires (HTTP/1.0)

http
Expires: Thu, 01 Dec 2024 16:00:00 GMT
  • 绝对时间,受本地时间影响
  • 已被 Cache-Control 取代

2.2 Cache-Control (HTTP/1.1)

http
Cache-Control: max-age=31536000
指令说明
max-age=<seconds>缓存有效时长 (秒)
s-maxage=<seconds>CDN 缓存时长,优先级高于 max-age
no-cache使用缓存前必须验证 (走协商缓存)
no-store完全不缓存
private仅浏览器缓存
public允许代理服务器缓存
immutable资源不会变化,刷新不会重新验证

2.3 常见配置示例

http
# HTML - 不缓存
Cache-Control: no-cache

# JS/CSS - 带 hash,长期缓存
Cache-Control: max-age=31536000, immutable

# API - 不缓存
Cache-Control: no-store

# 图片 - 7 天
Cache-Control: max-age=604800

2.4 强缓存命中

请求 → 检查 Cache-Control/Expires

      未过期 → 直接返回缓存 (200 from cache)

      已过期 → 进入协商缓存

三、协商缓存

3.1 Last-Modified / If-Modified-Since

http
# 首次响应
Last-Modified: Wed, 01 Dec 2024 10:00:00 GMT

# 再次请求
If-Modified-Since: Wed, 01 Dec 2024 10:00:00 GMT

# 服务器响应
# 未修改 → 304 Not Modified
# 已修改 → 200 + 新资源

缺点:

  • 精度只到秒级
  • 文件内容没变但修改时间变了,会重新下载

3.2 ETag / If-None-Match

http
# 首次响应
ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"

# 再次请求
If-None-Match: "33a64df551425fcc55e4d42a148795d9f25f89d4"

# 服务器响应
# 匹配 → 304 Not Modified
# 不匹配 → 200 + 新资源

ETag 类型:

  • 强 ETag: "abc123" - 字节完全相同
  • 弱 ETag: W/"abc123" - 语义相同即可

3.3 优先级

ETag > Last-Modified
Cache-Control > Expires

四、缓存流程图

请求资源

有缓存? ─── No ──→ 发送请求 → 获取响应 → 存入缓存 → 返回

   Yes

强缓存有效? ─── Yes ──→ 返回缓存 (200 from cache)

   No

协商缓存: 发送请求 (带 If-None-Match / If-Modified-Since)

资源变化? ─── No ──→ 返回 304 → 使用缓存

   Yes

返回 200 + 新资源 → 更新缓存

五、Service Worker 缓存

5.1 基本用法

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

// sw.js
const CACHE_NAME = 'v1';
const urlsToCache = ['/', '/styles/main.css', '/script/main.js'];

// 安装时缓存
self.addEventListener('install', (event) => {
  event.waitUntil(
    caches.open(CACHE_NAME)
      .then((cache) => cache.addAll(urlsToCache))
  );
});

// 请求拦截
self.addEventListener('fetch', (event) => {
  event.respondWith(
    caches.match(event.request)
      .then((response) => response || fetch(event.request))
  );
});

5.2 缓存策略

Cache First (缓存优先)

javascript
self.addEventListener('fetch', (event) => {
  event.respondWith(
    caches.match(event.request)
      .then((cached) => cached || fetch(event.request))
  );
});

Network First (网络优先)

javascript
self.addEventListener('fetch', (event) => {
  event.respondWith(
    fetch(event.request)
      .catch(() => caches.match(event.request))
  );
});

Stale While Revalidate (返回缓存,后台更新)

javascript
self.addEventListener('fetch', (event) => {
  event.respondWith(
    caches.open(CACHE_NAME).then((cache) => {
      return cache.match(event.request).then((cached) => {
        const fetchPromise = fetch(event.request).then((network) => {
          cache.put(event.request, network.clone());
          return network;
        });
        return cached || fetchPromise;
      });
    })
  );
});

六、实践建议

6.1 静态资源策略

# HTML
Cache-Control: no-cache  (每次协商)

# 带 hash 的 JS/CSS/图片
Cache-Control: max-age=31536000, immutable

# 不带 hash 的资源
Cache-Control: max-age=86400  (1天)

6.2 Nginx 配置示例

nginx
# HTML 不缓存
location ~* \.html$ {
  add_header Cache-Control "no-cache";
}

# 带 hash 的静态资源
location ~* \.(js|css|png|jpg|gif|ico|woff2)$ {
  add_header Cache-Control "max-age=31536000, immutable";
}

七、高频面试题

Q1: 强缓存和协商缓存的区别?

对比项强缓存协商缓存
是否发请求
状态码200 (from cache)304
HeaderCache-Control, ExpiresETag, Last-Modified

Q2: ETag 和 Last-Modified 的区别?

对比项ETagLast-Modified
精度内容级别秒级
计算需要计算 hash读取文件属性
优先级

Q3: no-cache 和 no-store 的区别?

  • no-cache: 使用缓存前必须验证 (走协商缓存)
  • no-store: 完全不缓存

Q4: 如何实现资源更新?

  1. 文件名添加 hash: app.abc123.js
  2. HTML 不缓存或协商缓存
  3. 静态资源长期强缓存

前端面试知识库