PWA开发指南
Web App Manifest (manifest.json)
{
"name": "我的应用",
"short_name": "MyApp",
"description": "一个渐进式 Web 应用",
"start_url": "/",
"scope": "/",
"display": "standalone",
"orientation": "portrait",
"theme_color": "#6c63ff",
"background_color": "#1c2035",
"icons": [
{ "src": "/icons/icon-192.png", "sizes": "192x192", "type": "image/png" },
{ "src": "/icons/icon-512.png", "sizes": "512x512", "type": "image/png" },
{ "src": "/icons/icon-maskable.png", "sizes": "512x512", "type": "image/png", "purpose": "maskable" }
]
}
<!-- 在 HTML head 中引用 -->
<link rel="manifest" href="/manifest.json">
<meta name="theme-color" content="#6c63ff">
Service Worker 注册
if ('serviceWorker' in navigator) {
window.addEventListener('load', async () => {
try {
const reg = await navigator.serviceWorker.register('/sw.js', {
scope: '/'
});
console.log('SW 已注册:', reg.scope);
} catch (err) {
console.error('SW 注册失败:', err);
}
});
}
缓存策略
// sw.js — 缓存优先(静态资源)
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request).then(cached => {
return cached || fetch(event.request);
})
);
});
// 网络优先(API 请求)
self.addEventListener('fetch', (event) => {
event.respondWith(
fetch(event.request)
.then(response => {
const clone = response.clone();
caches.open('api-cache').then(cache => cache.put(event.request, clone));
return response;
})
.catch(() => caches.match(event.request))
);
});
缓存策略参考
| 策略 | 最适合 | 新鲜度 |
|---|---|---|
| 缓存优先 | 静态资源、字体、图片 | 旧直到更新 |
| 网络优先 | API 数据、动态内容 | 始终最新,降级到缓存 |
| 过期同时重新验证 | 偶尔变化的页面 | 快速 + 下次访问时刷新 |
| 仅缓存 | 预缓存资源 | 静态 |
| 仅网络 | 实时数据、认证端点 | 始终最新,无离线 |