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; }) ) );});Network Only(仅走网络)
Section titled “Network Only(仅走网络)”始终从网络获取,service worker 不做任何缓存。
适用于: 绝不能使用旧版本的请求:埋点上报、POST 写操作、支付请求。本质上是透传。
Cache Only(仅用缓存)
Section titled “Cache Only(仅用缓存)”只从缓存返回内容;若缓存中没有,请求直接失败。
适用于: 在 install 阶段预缓存、确保一定存在的资源(离线外壳)。切勿用于动态或用户专属内容。
策略选择参考
Section titled “策略选择参考”| 资源类型 | 推荐策略 |
|---|---|
| 应用外壳(HTML、CSS、核心 JS) | Cache First,配合带版本号的缓存名称 |
带版本号的静态资源(/assets/main.abc123.js) |
Cache First——URL 本身在部署时会变化 |
| API 响应、需要及时更新的页面 | Network First,搭配离线兜底 |
| 头像、缩略图、次要字体 | Stale-While-Revalidate |
| 鉴权、支付、埋点 | Network Only |
| 离线兜底页面 | Cache Only(在 install 阶段预缓存) |
使用 Workbox
Section titled “使用 Workbox”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 在网络失败时返回可用响应而非浏览器报错。详见离线兜底。
- Cache API —
caches.open()、cache.put()与cache.match()基础 API - Fetch 事件与路由 —
fetch事件如何到达 service worker - Workbox — 生产可用的策略实现
- 离线兜底 — 提供有意义的离线页面