1.1 核心定位
Service Worker(简称 SW)是 W3C 标准定义的浏览器原生 API,本质上是一个独立运行的后台脚本。它充当网页应用与网络之间的智能代理,能够拦截、处理和响应所有网络请求。
工作原理示意:
┌─────────────┐ ┌──────────────┐ ┌─────────────┐
│ 网页应用 │ ←→ │ Service Worker│ ←→ │ 网络/缓存 │
└─────────────┘ └──────────────┘ └─────────────┘
↑
请求拦截与决策
1.2 核心能力清单
| 能力维度 | 具体功能 | 应用场景 |
|---|---|---|
| 请求拦截 | 监听 fetch 事件,自主决定响应来源 | 缓存策略、离线 fallback |
| 缓存管理 | 操作 Cache Storage API | 静态资源预存、动态缓存 |
| 离线支持 | 无网络时返回缓存内容 | PWA 离线可用 |
| 消息推送 | 接收 Push API 通知 | 消息提醒、营销推送 |
| 后台同步 | 网络恢复后同步数据 | 表单提交、数据同步 |
1.3 解决的核心痛点
传统 Web 应用在以下场景存在明显短板:
❌ 网络断开 → 页面白屏,完全不可用
❌ 弱网环境 → 加载缓慢,用户体验差
❌ 重复请求 → 静态资源每次都从网络拉取
❌ 无法推送 → 用户离开后无法触达
Service Worker 的出现,让 Web 应用首次具备了类原生 App 的离线能力和推送能力,成为 PWA(渐进式 Web 应用)三大核心特性(可靠、快速、可安装)的技术基石。
1.4 原生实现示例
下面是一个完整的 Service Worker 原生实现,展示预缓存和 Cache First 策略:
// sw.js - 放置于项目根目录
const CACHE_VERSION = 'v1';
const CACHE_NAME = `app-cache-${CACHE_VERSION}`;
// 需要预缓存的核心资源
const PRECACHE_ASSETS = [
'/',
'/index.html',
'/styles/main.css',
'/scripts/app.js',
'/images/logo.png'
];
// ─── 安装阶段:预缓存资源 ───
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_NAME)
.then((cache) => {
console.log('[SW] 预缓存资源中...');
return cache.addAll(PRECACHE_ASSETS);
})
.then(() => self.skipWaiting()) // 跳过等待,立即激活
);
});
// ─── 激活阶段:清理旧版本缓存 ───
self.addEventListener('activate', (event) => {
event.waitUntil(
caches.keys()
.then((cacheNames) => {
return Promise.all(
cacheNames
.filter((name) => name !== CACHE_NAME)
.map((name) => caches.delete(name))
);
})
.then(() => self.clients.claim()) // 立即接管所有页面
);
});
// ─── 请求拦截:Cache First 策略 ───
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request)
.then((cached) => {
if (cached) {
return cached; // 缓存命中,直接返回
}
// 缓存未命中,请求网络
return fetch(event.request)
.then((response) => {
// 只缓存有效的 GET 请求
if (!response || response.status !== 200 || response.type !== 'basic') {
return response;
}
// 克隆响应(响应流只能消费一次)
const clone = response.clone();
caches.open(CACHE_NAME)
.then((cache) => cache.put(event.request, clone));
return response;
})
.catch(() => {
// 网络失败,返回离线页面
return caches.match('/offline.html');
});
})
);
});
注册 Service Worker(主应用代码):
// main.js
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/sw.js')
.then((reg) => console.log('[SW] 注册成功', reg.scope))
.catch((err) => console.error('[SW] 注册失败', err));
});
}
1.5 原生实现的挑战
虽然原生 API 功能强大,但实际开发中面临诸多困难:
- 生命周期管理复杂:install → activate → idle 状态转换需手动处理
- 边缘场景繁琐:范围请求、opaque 响应、跨域问题需单独处理
- 缓存策略重复:每个项目都要重写类似的缓存逻辑
- 版本升级麻烦:旧缓存清理、资源清单维护容易出错
- 调试体验差:缺乏友好的开发工具和错误提示
二、Workbox:Service Worker 的生产级封装
2.1 什么是 Workbox?
Workbox 是 Google 官方维护的 JavaScript 工具集,专为简化 Service Worker 开发而设计。它不是替代 Service Worker,而是提供更高层的抽象和最佳实践封装。
核心定位:
Service Worker = 底层 API(手动挡)
Workbox = 上层封装(自动挡 + 智能辅助)
2.2 核心模块架构
┌─────────────────────────────────────────────────────────┐
│ Workbox 生态 │
├─────────────┬─────────────┬─────────────┬───────────────┤
│ precaching │ routing │ strategies│ plugins │
│ 预缓存模块 │ 路由匹配 │ 缓存策略 │ 插件系统 │
├─────────────┼─────────────┼─────────────┼───────────────┤
│ expiration │ background│ broadcast │ cacheable │
│ 过期管理 │ 后台同步 │ 更新通知 │ 响应过滤 │
└─────────────┴─────────────┴─────────────┴───────────────┘
2.3 原生 SW vs Workbox 对比
| 维度 | 原生 Service Worker | Workbox 方案 |
|---|---|---|
| 代码量 | 200+ 行(复杂策略) | 50 行左右配置 |
| 缓存策略 | 手动实现各种策略 | 内置 5 种标准策略 |
| 版本管理 | 手动清理旧缓存 | 自动版本检测和清理 |
| 资源清单 | 手动维护文件列表 | 构建时自动生成 |
| 调试体验 | 基础 DevTools | 开发模式 + 日志增强 |
| 学习曲线 | 陡峭,需理解底层 | 平缓,配置即可用 |
| 生产采用率 | 小型 demo 为主 | 54%+ PWA 项目使用 |
2.4 Workbox 生产级实现
以下是 2026 年主流的项目配置方式(基于 Vite + vite-plugin-pwa):
// vite.config.ts
import { defineConfig } from 'vite'
import { VitePWA } from 'vite-plugin-pwa'
export default defineConfig({
plugins: [
VitePWA({
registerType: 'autoUpdate', // 自动更新策略
workbox: {
// 自动预缓存构建产物
globPatterns: ['**/*.{js,css,html,png,svg,ico,webp}'],
// 运行时缓存策略配置
runtimeCaching: [
// ─── 静态图片:CacheFirst + 过期控制 ───
{
urlPattern: /\.(?:png|jpg|jpeg|svg|webp|ico)$/i,
handler: 'CacheFirst',
options: {
cacheName: 'static-images',
expiration: {
maxEntries: 60,
maxAgeSeconds: 30 * 24 * 60 * 60, // 30 天
},
cacheableResponse: {
statuses: [0, 200],
},
},
},
// ─── API 请求:StaleWhileRevalidate ───
{
urlPattern: ({ url }) => url.pathname.startsWith('/api/'),
handler: 'StaleWhileRevalidate',
options: {
cacheName: 'api-responses',
plugins: [
{
cacheWillUpdate: async ({ response }) => {
// 只缓存成功响应
return response?.status === 200 ? response : null;
},
},
{
expiration: {
maxEntries: 50,
maxAgeSeconds: 24 * 60 * 60, // 1 天
},
},
],
},
},
// ─── 页面导航:NetworkFirst ───
{
urlPattern: ({ request }) => request.mode === 'navigate',
handler: 'NetworkFirst',
options: {
cacheName: 'page-navigations',
networkTimeoutSeconds: 3, // 3 秒超时 fallback 到缓存
},
},
],
},
// 开发环境也启用 SW(便于调试)
devOptions: {
enabled: true,
type: 'module',
},
// PWA manifest 配置
manifest: {
name: '我的 PWA 应用',
short_name: 'MyPWA',
theme_color: '#ffffff',
display: 'standalone',
},
}),
],
})
客户端注册与更新提示:
// main.js
import { registerSW } from 'virtual:pwa-register'
const updateServiceWorker = registerSW({
// 新版本可用时的回调
onNeedRefresh() {
if (confirm('检测到新版本,是否立即更新?')) {
updateServiceWorker(true)
}
},
// 离线就绪回调
onOfflineReady() {
console.log('✅ 应用已支持离线使用')
// 可显示 toast 提示用户
},
// 更新错误处理
onRegisterError(error) {
console.error('SW 注册失败:', error)
},
})
三、五大缓存策略详解
Workbox 内置了 5 种标准缓存策略,覆盖绝大多数场景:
| 策略名称 | 工作流程 | 适用场景 |
|---|---|---|
| CacheFirst | 缓存 → 网络 → fallback | 静态资源(图片、字体) |
| NetworkFirst | 网络 → 缓存 → fallback | 动态内容、HTML 页面 |
| StaleWhileRevalidate | 缓存立即返回 + 后台更新网络 | API 数据、频繁变更内容 |
| NetworkOnly | 仅网络请求 | 敏感数据、实时信息 |
| CacheOnly | 仅缓存读取 | 离线专属资源 |
策略选择建议:
静态资源(JS/CSS/图片) → CacheFirst
API 接口数据 → StaleWhileRevalidate
HTML 页面导航 → NetworkFirst
实时敏感数据 → NetworkOnly
离线专用资源 → CacheOnly
四、选型建议与最佳实践
4.1 何时使用原生 Service Worker?
✅ 适合场景:
• 学习 Service Worker 底层原理
• 极致性能调优需求
• 超小型 demo 或实验项目
• 需要完全自定义生命周期逻辑
❌ 不推荐场景:
• 生产环境大型项目
• 团队协作开发
• 需要快速迭代上线
4.2 何时使用 Workbox?
✅ 强烈推荐场景:
• 99% 的生产环境 PWA 项目
• 中大型团队协作
• 需要多种缓存策略组合
• 追求开发效率和可维护性
📦 主流集成方案:
• Vite → vite-plugin-pwa
• Webpack → workbox-webpack-plugin
• Next.js → next-pwa
• Angular → @angular/pwa
4.3 生产环境检查清单
□ Service Worker 已正确注册且 scope 合理
□ 预缓存资源清单已自动生成并版本化
□ 运行时缓存策略已按资源类型分类配置
□ 缓存过期和数量限制已设置(防膨胀)
□ 版本更新通知机制已实现(用户感知)
□ 离线 fallback 页面已准备
□ 开发环境 SW 已启用(便于调试)
□ Lighthouse PWA 评分已验证
五、总结:核心差异一目了然
┌─────────────────────────────────────────────────────────────┐
│ Service Worker vs Workbox 核心对比 │
├──────────────────────────┬──────────────────────────────────┤
│ Service Worker │ Workbox │
├──────────────────────────┼──────────────────────────────────┤
│ 浏览器原生 API │ Google 官方工具库 │
│ 底层手动实现 │ 高层抽象封装 │
│ 生命周期需手动管理 │ 生命周期自动处理 │
│ 缓存策略自己写 │ 内置 5 种标准策略 │
│ 资源清单手动维护 │ 构建时自动生成 │
│ 适合学习和极致定制 │ 适合 99% 生产项目 │
│ 代码量大、易出错 │ 配置简洁、bug 少 │
└──────────────────────────┴──────────────────────────────────┘
一句话总结:
Service Worker 是 PWA 离线能力的发动机,Workbox 是让这台发动机更好用的智能变速箱。对于绝大多数项目,直接使用 Workbox 是更明智的选择——把时间花在业务创新上,而不是重复造轮子。

随时随地看视频