1. Service Worker 是什么?(补充具体代码例子)
Service Worker(简称 SW)是浏览器原生提供的一个 JavaScript API,本质上是运行在后台的代理脚本(proxy server)。它像一个“中间人”,坐在网页、浏览器缓存和真实网络之间。主要职责:
- 拦截所有网络请求(fetch 事件)
- 决定怎么响应:直接从缓存返回、去网络取、组合两者、甚至伪造响应
- 管理缓存(Cache Storage API)
- 实现离线支持、推送通知、后台同步等 PWA 高级能力
它解决了什么核心问题?
- 传统网页一断网就彻底崩(白屏、无法交互)
- 加载慢、重复请求多(尤其静态资源)
- 无法像原生 App 一样推送消息或在后台完成未发出的请求
- 弱网/无网场景下用户体验差 → 流失高
一句话:Service Worker 是 PWA “可靠 + 快速 + 可安装”三大支柱中最核心的引擎,没有它就没有真正的离线 PWA。
但原生写法很底层:生命周期(install → activate)、缓存管理、策略实现、边缘 case(范围请求、opaque 响应、跨域等)都需要自己手写,容易出错、代码量大、维护难。
原生 Service Worker 基本示例(一个简单的预缓存 + Cache First 策略,适合学习理解底层):
// sw.js(放在项目根目录)
const CACHE_NAME = 'my-pwa-cache-v1';
const urlsToCache = [
'/', // 首页
'/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('Opened cache');
return cache.addAll(urlsToCache);
})
.catch(err => console.error('Precache failed:', err))
);
// 跳过等待,直接激活(常见优化)
self.skipWaiting();
});
// 激活阶段:清理旧缓存
self.addEventListener('activate', event => {
const cacheWhitelist = [CACHE_NAME];
event.waitUntil(
caches.keys().then(cacheNames => {
return Promise.all(
cacheNames.map(cacheName => {
if (cacheWhitelist.indexOf(cacheName) === -1) {
return caches.delete(cacheName);
}
})
);
})
);
// 立即接管所有客户端页面
self.clients.claim();
});
// 拦截请求:简单的 Cache First 策略
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request)
.then(cachedResponse => {
// 缓存命中,直接返回
if (cachedResponse) {
return cachedResponse;
}
// 否则走网络,并尝试缓存响应(只缓存成功的 GET 请求)
return fetch(event.request).then(networkResponse => {
if (!networkResponse || networkResponse.status !== 200 || networkResponse.type !== 'basic') {
return networkResponse;
}
// 克隆响应(因为响应流只能消费一次)
const responseToCache = networkResponse.clone();
caches.open(CACHE_NAME)
.then(cache => {
cache.put(event.request, responseToCache);
});
return networkResponse;
}).catch(() => {
// 网络失败,返回离线兜底页面(可选)
return caches.match('/offline.html');
});
})
);
});
注册方式(在主 JS 文件中):
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/sw.js')
.then(reg => console.log('SW registered', reg))
.catch(err => console.error('SW registration failed', err));
});
}
这个例子展示了原生 SW 的完整生命周期和基本缓存逻辑,但你会发现:策略一复杂起来,代码就爆炸了。
2. Workbox 是什么?(补充具体代码例子)
Workbox 是 Google 官方开源的一套 JavaScript 库(多个模块组成),专门为 Service Worker 设计的“生产级工具包”。它不取代 Service Worker,而是站在 Service Worker 的肩膀上,提供更高层的抽象和最佳实践封装。
核心模块包括:
- 预缓存(precaching)自动生成清单
- 路由 + 策略(routing + 5大策略:CacheFirst、NetworkFirst、StaleWhileRevalidate 等)
- 过期管理、数量限制、广播更新
- 后台同步、缓存清理等插件
它解决了什么问题?(针对原生 SW 的痛点)
| 痛点(原生 Service Worker) | Workbox 如何解决 | 实际收益(2026 年生产视角) |
|---|---|---|
| 写 fetch 事件逻辑很繁琐、容易漏边缘 case | 内置策略类 + 插件系统,几行配置就实现复杂缓存 | 代码量减少 70%+,bug 少很多 |
| 缓存过期、版本冲突、手动清理旧缓存难搞 | ExpirationPlugin、Cleanup 等插件自动处理 | 缓存不会无限膨胀,版本更新更可靠 |
| 预缓存构建产物需要自己维护清单 | vite-plugin-pwa / workbox-webpack-plugin 自动生成 __WB_MANIFEST | 构建一次,清单自动更新 |
| 策略选择和组合麻烦(尤其是混合使用) | runtimeCaching 配置数组,按 URL/类型一目了然匹配策略 | 生产级策略组合 10 分钟搞定 |
| 调试难、没有好用的开发体验 | dev 模式自动注入、广播更新、Lighthouse 友好 | 开发和调试效率翻倍 |
| 重复造轮子(每个人都写类似缓存逻辑) | 内置最佳实践 + 社区验证过的插件生态 | 54%+ 移动 PWA 在用,框架官方推荐(Vite/Next/Angular 等) |
一句话总结区别:
- Service Worker = 浏览器提供的“发动机” → 功能强大,但原始、手动挡,开起来费力且容易翻车。
- Workbox = Google 给的“自动挡 + 导航 + 辅助驾驶系统” → 让你把精力放在业务上,而不是跟生命周期和缓存边缘 case 死磕。
99% 的真实项目(尤其是中大型)都应该直接用 Workbox,除非你在做底层研究、极致性能调优或极小 demo。
Workbox 生产级示例(使用 vite-plugin-pwa,2026 年主流方式,基于 Workbox v7.4+):
// 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}'], // 自动预缓存构建产物
runtimeCaching: [
// 图片:CacheFirst + 过期控制
{
urlPattern: /\.(?:png|jpg|jpeg|svg|webp|ico)$/,
handler: 'CacheFirst',
options: {
cacheName: 'images-cache',
expiration: {
maxEntries: 60,
maxAgeSeconds: 30 * 24 * 60 * 60, // 30 天
},
},
},
// API:StaleWhileRevalidate(快速显示旧数据 + 后台更新)
{
urlPattern: ({ url }) => url.pathname.startsWith('/api/'),
handler: 'StaleWhileRevalidate',
options: {
cacheName: 'api-cache',
plugins: [
{
cacheWillUpdate: async ({ response }) => {
// 只缓存 0/200 响应
return response.status === 0 || response.status === 200 ? response : null;
},
},
{
expiration: {
maxEntries: 50,
maxAgeSeconds: 24 * 60 * 60, // 1 天
},
},
],
},
},
// 页面导航:NetworkFirst(优先新鲜内容)
{
urlPattern: ({ request }) => request.mode === 'navigate',
handler: 'NetworkFirst',
options: {
cacheName: 'pages-cache',
networkTimeoutSeconds: 3,
},
},
],
},
devOptions: { enabled: true }, // 本地开发也启用 SW
}),
],
})
客户端注册 & 优雅更新(main.js):
import { registerSW } from 'virtual:pwa-register'
const updateSW = registerSW({
onNeedRefresh() {
// 可以显示 toast 或弹窗提示用户更新
if (confirm('有新版本可用,是否立即更新?')) {
updateSW(true)
}
},
onOfflineReady() {
console.log('应用已准备好离线使用')
},
})
用 Workbox 后,sw.js 几乎不用手写,插件自动生成 + 维护,策略通过配置数组声明即可。