什么是 PWA?Service Worker 的作用是什么?
PWA(Progressive Web App)
渐进式 Web 应用,结合 Web 和原生应用的优点:
- 可安装(添加到主屏幕)
- 离线可用
- 推送通知
- 后台同步
Service Worker
独立于主线程的后台脚本,充当浏览器与网络之间的代理。
特点:
- 在独立线程中运行
- 不能操作 DOM
- 可拦截网络请求
- 可使用 Cache API 和 IndexedDB
- 必须在 HTTPS 下运行
基本使用
注册 Service Worker:
// main.js
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/sw.js')
.then(reg => console.log('SW registered'))
.catch(err => console.log('SW failed:', err))
})
}Service Worker 生命周期:
// sw.js
const CACHE_NAME = 'v1'
const ASSETS = ['/', '/index.html', '/styles.css', '/app.js']
// 安装:预缓存资源
self.addEventListener('install', (e) => {
e.waitUntil(
caches.open(CACHE_NAME).then(cache => cache.addAll(ASSETS))
)
self.skipWaiting() // 立即激活
})
// 激活:清理旧缓存
self.addEventListener('activate', (e) => {
e.waitUntil(
caches.keys().then(keys =>
Promise.all(keys.filter(k => k !== CACHE_NAME).map(k => caches.delete(k)))
)
)
self.clients.claim()
})
// 拦截请求
self.addEventListener('fetch', (e) => {
e.respondWith(
caches.match(e.request).then(cached => {
return cached || fetch(e.request).then(response => {
// 缓存新资源
const clone = response.clone()
caches.open(CACHE_NAME).then(cache => cache.put(e.request, clone))
return response
}).catch(() => caches.match('/offline.html'))
})
)
})缓存策略
// 1. Cache First(适合静态资源)
async function cacheFirst(request) {
const cached = await caches.match(request)
return cached || fetch(request)
}
// 2. Network First(适合 API 请求)
async function networkFirst(request) {
try {
const response = await fetch(request)
const clone = response.clone()
const cache = await caches.open('api-cache')
cache.put(request, clone)
return response
} catch {
return caches.match(request)
}
}
// 3. Stale While Revalidate(先缓存后更新)
async function staleWhileRevalidate(request) {
const cache = await caches.open('dynamic')
const cached = await cache.match(request)
const fetchPromise = fetch(request).then(response => {
cache.put(request, response.clone())
return response
})
return cached || fetchPromise
}manifest.json(可安装)
{
"name": "My PWA",
"short_name": "MyApp",
"start_url": "/",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#4f46e5",
"icons": [
{
"src": "/icon-192.png",
"sizes": "192x192",
"type": "image/png"
}
]
}<link rel="manifest" href="/manifest.json">推送通知
// 请求权限
const permission = await Notification.requestPermission()
// 订阅推送
const reg = await navigator.serviceWorker.ready
const subscription = await reg.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: VAPID_PUBLIC_KEY
})
// Service Worker 接收推送
self.addEventListener('push', (e) => {
e.waitUntil(
self.registration.showNotification('标题', {
body: e.data.text(),
icon: '/icon.png'
})
)
})适用场景
- ✅ 内容型网站(新闻、电商)
- ✅ 需要离线功能的应用
- ✅ 需要推送通知
- ❌ 需要复杂原生功能(蓝牙、NFC 等)