pwa-expert
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseProgressive Web App Expert
PWA开发专家
Build installable, offline-capable web apps with Service Workers, smart caching, and native-like experiences.
借助Service Worker、智能缓存和类原生体验,构建可安装、支持离线的Web应用。
When to Use This Skill
适用场景
- Making a web app installable on mobile/desktop
- Implementing offline functionality
- Setting up Service Worker caching strategies
- Handling install prompts ()
beforeinstallprompt - Background sync for offline-first apps
- Managing PWA update flows
- Creating web app manifests
- 让Web应用可在移动设备/桌面端安装
- 实现离线功能
- 配置Service Worker缓存策略
- 处理安装提示(事件)
beforeinstallprompt - 为离线优先应用配置后台同步
- 管理PWA更新流程
- 创建Web应用清单
When NOT to Use This Skill
不适用场景
- Native app development → Use React Native, Flutter, or native SDKs
- General web performance → Use Lighthouse/performance auditing tools
- Server-side rendering issues → Use Next.js/framework-specific docs
- Push notifications only → Consider dedicated push notification services
- Simple static sites → PWA overhead may not be worth it
- 原生应用开发 → 使用React Native、Flutter或原生SDK
- 通用Web性能优化 → 使用Lighthouse或性能审计工具
- 服务端渲染问题 → 使用Next.js或对应框架的文档
- 仅需推送通知 → 考虑专用的推送通知服务
- 简单静态网站 → PWA的额外开销得不偿失
Core Concepts
核心概念
What Makes a PWA Installable
PWA可安装的条件
- HTTPS (or localhost for dev)
- Web App Manifest with required fields
- Service Worker with fetch handler
- Icons (192×192 and 512×512 minimum)
- HTTPS(开发环境可使用localhost)
- 包含必填字段的Web应用清单(Web App Manifest)
- 带有fetch处理器的Service Worker
- 图标(最小尺寸192×192和512×512)
The PWA Stack
PWA技术栈
┌─────────────────────────────────────────┐
│ Your App (React/Next.js) │
├─────────────────────────────────────────┤
│ Service Worker (sw.js) │
│ ┌─────────────┐ ┌─────────────────┐ │
│ │ Cache │ │ Network Fetch │ │
│ │ Storage │ │ Handling │ │
│ └─────────────┘ └─────────────────┘ │
├─────────────────────────────────────────┤
│ manifest.json │
│ (App identity, icons, display mode) │
└─────────────────────────────────────────┘┌─────────────────────────────────────────┐
│ 你的应用(React/Next.js) │
├─────────────────────────────────────────┤
│ Service Worker (sw.js) │
│ ┌─────────────┐ ┌─────────────────┐ │
│ │ 缓存(Cache) │ │ 网络请求处理 │ │
│ │ 存储(Storage)│ │ │ │
│ └─────────────┘ └─────────────────┘ │
├─────────────────────────────────────────┤
│ manifest.json │
│ (应用标识、图标、显示模式) │
└─────────────────────────────────────────┘Web App Manifest
Web应用清单(Web App Manifest)
Complete manifest.json
完整的manifest.json示例
json
{
"name": "Junkie Buds 4 Life",
"short_name": "JB4L",
"description": "Recovery support app",
"start_url": "/",
"scope": "/",
"display": "standalone",
"orientation": "portrait-primary",
"background_color": "#1a1410",
"theme_color": "#1a1410",
"icons": [
{
"src": "/icons/icon-192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "any"
},
{
"src": "/icons/icon-512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "any"
},
{
"src": "/icons/icon-maskable-512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
}
],
"shortcuts": [
{
"name": "Find Meetings",
"short_name": "Meetings",
"url": "/meetings?source=shortcut",
"icons": [{ "src": "/icons/meetings-96.png", "sizes": "96x96" }]
}
]
}json
{
"name": "Junkie Buds 4 Life",
"short_name": "JB4L",
"description": "康复支持应用",
"start_url": "/",
"scope": "/",
"display": "standalone",
"orientation": "portrait-primary",
"background_color": "#1a1410",
"theme_color": "#1a1410",
"icons": [
{
"src": "/icons/icon-192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "any"
},
{
"src": "/icons/icon-512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "any"
},
{
"src": "/icons/icon-maskable-512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
}
],
"shortcuts": [
{
"name": "查找会议",
"short_name": "会议",
"url": "/meetings?source=shortcut",
"icons": [{ "src": "/icons/meetings-96.png", "sizes": "96x96" }]
}
]
}Display Modes
显示模式
| Mode | Description |
|---|---|
| No browser UI, full screen |
| App-like, no URL bar (recommended) |
| Some browser controls |
| Normal browser tab |
| 模式 | 描述 |
|---|---|
| 无浏览器UI,全屏显示 |
| 类应用样式,无地址栏(推荐) |
| 保留部分浏览器控件 |
| 普通浏览器标签页 |
Link in HTML
在HTML中引入
html
<head>
<link rel="manifest" href="/manifest.json" />
<meta name="theme-color" content="#1a1410" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<link rel="apple-touch-icon" href="/icons/apple-touch-icon.png" />
</head>html
<head>
<link rel="manifest" href="/manifest.json" />
<meta name="theme-color" content="#1a1410" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<link rel="apple-touch-icon" href="/icons/apple-touch-icon.png" />
</head>Service Worker Basics
Service Worker基础
Registration
注册
typescript
// lib/pwa.ts
export async function registerServiceWorker() {
if ('serviceWorker' in navigator) {
try {
const registration = await navigator.serviceWorker.register('/sw.js', {
scope: '/',
});
return registration;
} catch (error) {
console.error('SW registration failed:', error);
}
}
}
// Call on app mount
useEffect(() => {
registerServiceWorker();
}, []);typescript
// lib/pwa.ts
export async function registerServiceWorker() {
if ('serviceWorker' in navigator) {
try {
const registration = await navigator.serviceWorker.register('/sw.js', {
scope: '/',
});
return registration;
} catch (error) {
console.error('SW注册失败:', error);
}
}
}
// 在应用挂载时调用
useEffect(() => {
registerServiceWorker();
}, []);Basic Service Worker Structure
基础Service Worker结构
javascript
// public/sw.js
const CACHE_NAME = 'myapp-v1';
const STATIC_ASSETS = ['/', '/offline', '/manifest.json'];
// Install: Cache static assets
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_NAME).then((cache) => cache.addAll(STATIC_ASSETS))
);
self.skipWaiting();
});
// Activate: Clean old caches
self.addEventListener('activate', (event) => {
event.waitUntil(
caches.keys().then((keys) =>
Promise.all(keys.filter((k) => k !== CACHE_NAME).map((k) => caches.delete(k)))
)
);
self.clients.claim();
});
// Fetch: Handle requests (see references for strategies)
self.addEventListener('fetch', (event) => {
event.respondWith(handleFetch(event.request));
});See:for caching strategy implementationsreferences/service-worker-patterns.md
javascript
// public/sw.js
const CACHE_NAME = 'myapp-v1';
const STATIC_ASSETS = ['/', '/offline', '/manifest.json'];
// 安装:缓存静态资源
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_NAME).then((cache) => cache.addAll(STATIC_ASSETS))
);
self.skipWaiting();
});
// 激活:清理旧缓存
self.addEventListener('activate', (event) => {
event.waitUntil(
caches.keys().then((keys) =>
Promise.all(keys.filter((k) => k !== CACHE_NAME).map((k) => caches.delete(k)))
)
);
self.clients.claim();
});
// 网络请求:处理请求(缓存策略实现请参考参考文献)
self.addEventListener('fetch', (event) => {
event.respondWith(handleFetch(event.request));
});参考:中的缓存策略实现references/service-worker-patterns.md
Caching Strategies
缓存策略
| Strategy | Best For | Tradeoff |
|---|---|---|
| Cache-First | Static assets, fonts, images | Stale until cache updated |
| Network-First | API data, user content | Slower, needs connectivity |
| Stale-While-Revalidate | Balance freshness/speed | Background updates |
| Network-Only | Auth, real-time data | No offline support |
| Cache-Only | Versioned assets | Never updates |
See:for full implementationsreferences/service-worker-patterns.md
| 策略 | 适用场景 | 权衡点 |
|---|---|---|
| Cache-First | 静态资源、字体、图片 | 缓存未更新前内容可能过时 |
| Network-First | API数据、用户内容 | 速度较慢,需要网络连接 |
| Stale-While-Revalidate | 平衡新鲜度与速度 | 在后台更新内容 |
| Network-Only | 认证请求、实时数据 | 无离线支持 |
| Cache-Only | 版本化资源 | 永不自动更新 |
参考:中的完整实现references/service-worker-patterns.md
Install Prompts
安装提示
Handle the event to show a custom install UI:
beforeinstallprompttypescript
// Basic pattern
const [deferredPrompt, setDeferredPrompt] = useState(null);
useEffect(() => {
window.addEventListener('beforeinstallprompt', (e) => {
e.preventDefault();
setDeferredPrompt(e);
});
}, []);
const handleInstall = async () => {
if (deferredPrompt) {
deferredPrompt.prompt();
const { outcome } = await deferredPrompt.userChoice;
// outcome: 'accepted' or 'dismissed'
}
};See:for fullreferences/install-prompt.mdhook and componentusePWAInstall
处理事件以显示自定义安装界面:
beforeinstallprompttypescript
// 基础实现
const [deferredPrompt, setDeferredPrompt] = useState(null);
useEffect(() => {
window.addEventListener('beforeinstallprompt', (e) => {
e.preventDefault();
setDeferredPrompt(e);
});
}, []);
const handleInstall = async () => {
if (deferredPrompt) {
deferredPrompt.prompt();
const { outcome } = await deferredPrompt.userChoice;
// outcome值为'accepted'或'dismissed'
}
};参考:中的完整references/install-prompt.md钩子和组件实现usePWAInstall
Offline Experience
离线体验
Key patterns:
- Offline page fallback for navigation failures
- hook to detect connectivity
useOnlineStatus - Offline banner to inform users
See:for implementationsreferences/offline-handling.md
关键实现模式:
- 导航失败时的离线页面回退
- 钩子检测网络连接状态
useOnlineStatus - 离线提示横幅告知用户
参考:中的实现方案references/offline-handling.md
Background Sync
后台同步
Queue actions while offline, execute when connectivity returns:
javascript
// In Service Worker
self.addEventListener('sync', (event) => {
if (event.tag === 'sync-data') {
event.waitUntil(syncPendingData());
}
});
// In App - trigger sync
const registration = await navigator.serviceWorker.ready;
await registration.sync.register('sync-data');See:for full IndexedDB integrationreferences/background-sync.md
在离线时排队操作,恢复网络连接后执行:
javascript
// 在Service Worker中
self.addEventListener('sync', (event) => {
if (event.tag === 'sync-data') {
event.waitUntil(syncPendingData());
}
});
// 在应用中 - 触发同步
const registration = await navigator.serviceWorker.ready;
await registration.sync.register('sync-data');参考:中的IndexedDB集成完整实现references/background-sync.md
Update Flow
更新流程
Notify users when a new version is available:
typescript
// Basic pattern
registration.addEventListener('updatefound', () => {
const newWorker = registration.installing;
newWorker?.addEventListener('statechange', () => {
if (newWorker.state === 'installed' && navigator.serviceWorker.controller) {
// New version available - show update prompt
}
});
});See:forreferences/update-flow.mdhook and update strategiesusePWAUpdate
当有新版本可用时通知用户:
typescript
// 基础实现
registration.addEventListener('updatefound', () => {
const newWorker = registration.installing;
newWorker?.addEventListener('statechange', () => {
if (newWorker.state === 'installed' && navigator.serviceWorker.controller) {
// 新版本可用 - 显示更新提示
}
});
});参考:中的references/update-flow.md钩子和更新策略usePWAUpdate
Next.js Integration
Next.js集成
Options for Next.js PWA:
- next-pwa - Works with standard Next.js server
- Custom SW - Required for (static sites)
output: 'export' - Workbox CLI - Generate SW after build
See:for detailed configurationsreferences/nextjs-integration.md
Next.js中实现PWA的选项:
- next-pwa - 适用于标准Next.js服务
- 自定义SW - 当使用(静态站点)时必需
output: 'export' - Workbox CLI - 构建后生成Service Worker
参考:中的详细配置references/nextjs-integration.md
Quick Reference
快速参考
| Task | Solution |
|---|---|
| Check if installed | |
| Force SW update | |
| Clear all caches | |
| Check online | |
| Get SW registration | |
| Skip waiting | |
| Take control | |
| 任务 | 解决方案 |
|---|---|
| 检查应用是否已安装 | |
| 强制Service Worker更新 | |
| 清除所有缓存 | |
| 检查网络状态 | |
| 获取Service Worker注册实例 | |
| 跳过等待直接激活 | 在Service Worker中调用 |
| 立即控制所有客户端 | 在Service Worker中调用 |
Testing PWA
PWA测试
Chrome DevTools
Chrome开发者工具
- Application tab → Manifest, Service Workers, Cache Storage
- Lighthouse → PWA audit
- Network → Offline checkbox to simulate
- Application标签 → 查看Manifest、Service Workers、Cache Storage
- Lighthouse → 执行PWA审计
- Network标签 → 勾选Offline模拟离线环境
Debug Checklist
调试检查清单
- Manifest loads (Application → Manifest)
- SW registered (Application → Service Workers)
- Cache populated (Application → Cache Storage)
- Install prompt fires (Console for beforeinstallprompt)
- Offline page works (Network → Offline)
- Update flow works (trigger update, verify prompt)
- Manifest加载正常(Application → Manifest)
- Service Worker已注册(Application → Service Workers)
- 缓存已填充(Application → Cache Storage)
- 安装提示触发正常(控制台查看beforeinstallprompt事件)
- 离线页面可正常访问(Network → Offline)
- 更新流程正常(触发更新,验证提示)
References
参考文献
Detailed implementations in :
/references/- - Caching strategy implementations
service-worker-patterns.md - -
install-prompt.mdhook and install componentusePWAInstall - - Offline page, status hooks, banners
offline-handling.md - - Background sync with IndexedDB
background-sync.md - - Update detection and user prompts
update-flow.md - - Next.js PWA configuration options
nextjs-integration.md
详细实现方案位于目录:
/references/- - 缓存策略实现
service-worker-patterns.md - -
install-prompt.md钩子和安装组件usePWAInstall - - 离线页面、状态钩子、提示横幅
offline-handling.md - - 结合IndexedDB的后台同步
background-sync.md - - 更新检测和用户提示
update-flow.md - - Next.js PWA配置选项
nextjs-integration.md