# 选择缓存策略

> 如何为你的 PWA 选择正确的 Service Worker 缓存策略——缓存优先、网络优先、陈旧-while-重新验证，以及何时使用每种策略。

**一句话：** 正确的缓存策略取决于资源是静态的、动态的还是时间敏感的——以及用户对过时内容的容忍程度。

## 核心策略

Service Worker 拦截 `fetch` 事件并决定从哪里获取响应：缓存、网络或两者兼有。每种策略在速度、新鲜度和离线可用性之间做出不同的权衡。

### 缓存优先

先检查缓存；如果没有缓存内容则回退到网络。

```js
async function cacheFirst(request) {
  const cached = await caches.match(request);
  if (cached) return cached;
  return fetch(request);
}
```

**适用场景：** 静态资源（JS 包、CSS、图片、字体），在部署之间不会改变。一旦缓存，响应是即时的——即使离线。

### 网络优先

先尝试网络；如果请求失败或超时则回退到缓存。

```js
async function networkFirst(request) {
  try {
    const response = await fetch(request);
    const cache = await caches.open('dynamic');
    cache.put(request, response.clone());
    return response;
  } catch {
    return caches.match(request);
  }
}
```

**适用场景：** 动态内容（API 响应、用户数据），新鲜度很重要但仍需要离线回退。

### 陈旧-while-重新验证

立即返回缓存的响应，然后在后台获取新副本以供下次使用。

```js
async function staleWhileRevalidate(request) {
  const cached = await caches.match(request);
  const fetchPromise = fetch(request).then((response) => {
    const cache = caches.open('dynamic');
    cache.then((c) => c.put(request, response.clone()));
    return response;
  });
  return cached || fetchPromise;
}
```

**适用场景：** 半静态内容（UI 框架、文章列表），快速显示比显示最新版本更重要。

### 仅网络

始终使用网络，不使用缓存。

**适用场景：** 非 GET 请求（POST、PUT、DELETE）、实时数据（WebSocket 连接），或绝不能过时的资源。

### 仅缓存

仅从缓存提供服务；如果资源未缓存则失败。

**适用场景：** 在安装事件中预缓存的资源（应用外壳、离线回退页面）。

## 选择策略

| 资源类型 | 推荐策略 | 原因 |
|---|---|---|
| 应用外壳（HTML、CSS、JS） | 缓存优先 | 很少改变；首次访问后即时加载 |
| 图片和字体 | 缓存优先 | 使用哈希文件名时在部署之间不可变 |
| API 数据（用户特定） | 网络优先 | 必须新鲜；缓存提供离线回退 |
| API 数据（共享/半静态） | 陈旧-while-重新验证 | 快速显示缓存数据，后台更新 |
| 导航请求 | 网络优先 + 离线回退 | 在线时获取新鲜 HTML；离线时使用缓存外壳 |

## 基于时间的过期

对于既不是不可变的也不是关键的资源，在陈旧-while-重新验证或缓存优先中添加时间检查。将响应时间戳与缓存响应一起存储，如果缓存超过阈值则回退到网络：

```js
async function cacheWithExpiry(request, maxAgeSeconds) {
  const cached = await caches.match(request);
  if (cached) {
    const cachedDate = new Date(cached.headers.get('date'));
    if (Date.now() - cachedDate.getTime() < maxAgeSeconds * 1000) {
      return cached;
    }
  }
  return fetch(request);
}
```

## 实用检查清单

- [ ] 对带哈希的静态资源（JS、CSS、图片）使用缓存优先。
- [ ] 对 API 数据和用户特定内容使用网络优先。
- [ ] 对半静态列表和 UI 框架使用陈旧-while-重新验证。
- [ ] 在 `install` 事件中为应用外壳添加预缓存步骤。
- [ ] 设置包含版本字符串的缓存名称，以便部署时清除旧缓存。
- [ ] 限制缓存大小和过期时间以防止无限存储增长。
- [ ] 始终为导航请求提供离线回退页面。