跳转到内容

Service worker 缓存策略

一句话: 缓存策略是 service worker 在 fetch 事件触发时遵循的规则——先查缓存、先走网络,还是两者结合,以及以何种顺序。五种标准策略覆盖了 PWA 所需的所有离线与性能权衡场景。

Cache First(缓存优先,缺失时回退到网络)

Section titled “Cache First(缓存优先,缺失时回退到网络)”

先查缓存,命中则直接返回;未命中时从网络获取,并可选地将结果存入缓存。

适用于: 内容长期稳定的静态资源(应用外壳、字体、图片、带版本号的 JS 包)。缓存命中即刻响应;未命中则承受完整的网络往返延迟。

不适用于: 必须始终保持最新的资源(用户数据、价格、鉴权 Token)。

self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request).then((cached) => {
if (cached) return cached;
return fetch(event.request).then((response) => {
const clone = response.clone();
caches.open('v1').then((cache) => cache.put(event.request, clone));
return response;
});
})
);
});

Network First(网络优先,失败时回退到缓存)

Section titled “Network First(网络优先,失败时回退到缓存)”

优先尝试网络;成功则缓存并返回最新响应;网络失败时回退到缓存中已有的内容。

适用于: 希望内容及时但可以接受旧版离线兜底的页面(新闻流、仪表盘)。

不适用于: 大体积、变更不频繁的静态资源——每次请求都要支付网络开销。

self.addEventListener('fetch', (event) => {
event.respondWith(
fetch(event.request)
.then((response) => {
const clone = response.clone();
caches.open('v1').then((cache) => cache.put(event.request, clone));
return response;
})
.catch(() => caches.match(event.request))
);
});

Stale-While-Revalidate(旧版本立即响应,后台更新)

Section titled “Stale-While-Revalidate(旧版本立即响应,后台更新)”

立即返回缓存版本(速度快),同时在后台获取最新内容并更新缓存,供下次使用。

适用于: 速度优先于绝对新鲜度的资源(头像图片、非关键 API 数据、次要脚本)。用户始终能快速看到内容;后台请求保持缓存热度。

不适用于: 在同一次页面加载中,与更新版本并存会导致错误的资源。

self.addEventListener('fetch', (event) => {
event.respondWith(
caches.open('v1').then((cache) =>
cache.match(event.request).then((cached) => {
const networkFetch = fetch(event.request).then((response) => {
cache.put(event.request, response.clone());
return response;
});
return cached ?? networkFetch;
})
)
);
});

始终从网络获取,service worker 不做任何缓存。

适用于: 绝不能使用旧版本的请求:埋点上报、POST 写操作、支付请求。本质上是透传。

只从缓存返回内容;若缓存中没有,请求直接失败。

适用于:install 阶段预缓存、确保一定存在的资源(离线外壳)。切勿用于动态或用户专属内容。

资源类型 推荐策略
应用外壳(HTML、CSS、核心 JS) Cache First,配合带版本号的缓存名称
带版本号的静态资源(/assets/main.abc123.js Cache First——URL 本身在部署时会变化
API 响应、需要及时更新的页面 Network First,搭配离线兜底
头像、缩略图、次要字体 Stale-While-Revalidate
鉴权、支付、埋点 Network Only
离线兜底页面 Cache Only(在 install 阶段预缓存)

Workbox 将五种策略封装为可组合的类,无需手写 fetch 事件处理器:

import { registerRoute } from 'workbox-routing';
import { CacheFirst, NetworkFirst, StaleWhileRevalidate } from 'workbox-strategies';
import { CacheableResponsePlugin } from 'workbox-cacheable-response';
// 应用外壳——网络优先,带页面缓存
registerRoute(
({ request }) => request.mode === 'navigate',
new NetworkFirst({ cacheName: 'pages' })
);
// 图片——旧版立即响应
registerRoute(
({ request }) => request.destination === 'image',
new StaleWhileRevalidate({ cacheName: 'images' })
);
// 带版本号的静态资源——缓存优先,长期有效
registerRoute(
({ url }) => url.pathname.startsWith('/assets/'),
new CacheFirst({
cacheName: 'static-assets',
plugins: [new CacheableResponsePlugin({ statuses: [200] })],
})
);

完整配置详情请参阅 Workbox

  • 按资源类型选择策略,而非对所有资源一刀切。
  • 为缓存名称加版本号(如 shell-v3),并在 activate 中删除旧缓存。
  • 切勿缓存非幂等请求(POST、PUT、DELETE),除非是在故意构建后台同步队列。
  • Cache First 资源须将 URL 与内容哈希绑定,确保部署后不会从缓存中提供过期内容。
  • 添加明确的离线兜底页面,使 Network First 在网络失败时返回可用响应而非浏览器报错。详见离线兜底