跳转到内容

选择缓存策略

发布于 更新于

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

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

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

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

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

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

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 响应、用户数据),新鲜度很重要但仍需要离线回退。

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

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-重新验证或缓存优先中添加时间检查。将响应时间戳与缓存响应一起存储,如果缓存超过阈值则回退到网络:

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 事件中为应用外壳添加预缓存步骤。
  • 设置包含版本字符串的缓存名称,以便部署时清除旧缓存。
  • 限制缓存大小和过期时间以防止无限存储增长。
  • 始终为导航请求提供离线回退页面。