pwa-expert by erichowens/some_claude_skills
npx skills add https://github.com/erichowens/some_claude_skills --skill pwa-expert使用 Service Workers、智能缓存和类原生体验,构建可安装、支持离线功能的 Web 应用。
beforeinstallprompt) ┌─────────────────────────────────────────┐
│ 您的应用 (React/Next.js) │
├─────────────────────────────────────────┤
│ Service Worker (sw.js) │
│ ┌─────────────┐ ┌─────────────────┐ │
│ │ 缓存 │ │ 网络请求处理 │ │
│ │ 存储 │ │ │ │
│ └─────────────┘ └─────────────────┘ │
├─────────────────────────────────────────┤
│ manifest.json │
│ (应用标识、图标、显示模式) │
└─────────────────────────────────────────┘
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
{
"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" }]
}
]
}
| 模式 | 描述 |
|---|---|
fullscreen | 无浏览器 UI,全屏显示 |
standalone | 类应用模式,无地址栏(推荐) |
minimal-ui | 保留部分浏览器控件 |
browser | 普通浏览器标签页 |
<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>
// 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();
}, []);
// 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));
});
参见:
references/service-worker-patterns.md获取缓存策略实现
| 策略 | 最适合 | 权衡点 |
|---|---|---|
| Cache-First | 静态资源、字体、图像 | 缓存更新前内容可能过时 |
| Network-First | API 数据、用户内容 | 较慢,需要网络连接 |
| Stale-While-Revalidate | 平衡新鲜度与速度 | 后台更新 |
| Network-Only | 认证、实时数据 | 无离线支持 |
| Cache-Only | 版本化资源 | 永不更新 |
参见:
references/service-worker-patterns.md获取完整实现
处理 beforeinstallprompt 事件以显示自定义安装 UI:
// 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'
}
};
参见:
references/install-prompt.md获取完整的usePWAInstall钩子和组件
关键模式:
useOnlineStatus 钩子参见:
references/offline-handling.md获取实现
离线时排队操作,连接恢复后执行:
// 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');
参见:
references/background-sync.md获取完整的 IndexedDB 集成
当有新版本可用时通知用户:
// 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
}
});
});
参见:
references/update-flow.md获取usePWAUpdate钩子和更新策略
Next.js PWA 的选项:
output: 'export'(静态站点)必需参见:
references/nextjs-integration.md获取详细配置
| 任务 | 解决方案 |
|---|---|
| 检查是否已安装 | window.matchMedia('(display-mode: standalone)').matches |
| 强制 SW 更新 | registration.update() |
| 清除所有缓存 | caches.keys().then(keys => keys.forEach(k => caches.delete(k))) |
| 检查在线状态 | navigator.onLine |
| 获取 SW 注册 | navigator.serviceWorker.ready |
| 跳过等待 | 在 SW 中使用 self.skipWaiting() |
| 接管控制权 | 在 SW 中使用 self.clients.claim() |
/references/ 目录下的详细实现:
service-worker-patterns.md - 缓存策略实现install-prompt.md - usePWAInstall 钩子和安装组件offline-handling.md - 离线页面、状态钩子、横幅background-sync.md - 与 IndexedDB 的后台同步update-flow.md - 更新检测和用户提示nextjs-integration.md - Next.js PWA 配置选项每周安装数
125
代码仓库
GitHub 星标数
76
首次出现
Jan 24, 2026
安全审计
已安装于
gemini-cli114
opencode109
codex107
github-copilot103
cursor100
claude-code89
Build installable, offline-capable web apps with Service Workers, smart caching, and native-like experiences.
beforeinstallprompt)┌─────────────────────────────────────────┐
│ Your App (React/Next.js) │
├─────────────────────────────────────────┤
│ Service Worker (sw.js) │
│ ┌─────────────┐ ┌─────────────────┐ │
│ │ Cache │ │ Network Fetch │ │
│ │ Storage │ │ Handling │ │
│ └─────────────┘ └─────────────────┘ │
├─────────────────────────────────────────┤
│ manifest.json │
│ (App identity, icons, display mode) │
└─────────────────────────────────────────┘
{
"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" }]
}
]
}
| Mode | Description |
|---|---|
fullscreen | No browser UI, full screen |
standalone | App-like, no URL bar (recommended) |
minimal-ui | Some browser controls |
browser | Normal browser tab |
<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>
// 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();
}, []);
// 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:
references/service-worker-patterns.mdfor caching strategy implementations
| 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:
references/service-worker-patterns.mdfor full implementations
Handle the beforeinstallprompt event to show a custom install UI:
// 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:
references/install-prompt.mdfor fullusePWAInstallhook and component
Key patterns:
useOnlineStatus hook to detect connectivitySee:
references/offline-handling.mdfor implementations
Queue actions while offline, execute when connectivity returns:
// 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:
references/background-sync.mdfor full IndexedDB integration
Notify users when a new version is available:
// 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:
references/update-flow.mdforusePWAUpdatehook and update strategies
Options for Next.js PWA:
output: 'export' (static sites)See:
references/nextjs-integration.mdfor detailed configurations
| Task | Solution |
|---|---|
| Check if installed | window.matchMedia('(display-mode: standalone)').matches |
| Force SW update | registration.update() |
| Clear all caches | caches.keys().then(keys => keys.forEach(k => caches.delete(k))) |
| Check online | navigator.onLine |
| Get SW registration | navigator.serviceWorker.ready |
| Skip waiting | self.skipWaiting() in SW |
Detailed implementations in /references/:
service-worker-patterns.md - Caching strategy implementationsinstall-prompt.md - usePWAInstall hook and install componentoffline-handling.md - Offline page, status hooks, bannersbackground-sync.md - Background sync with IndexedDBupdate-flow.md - Update detection and user promptsnextjs-integration.md - Next.js PWA configuration optionsWeekly Installs
125
Repository
GitHub Stars
76
First Seen
Jan 24, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
gemini-cli114
opencode109
codex107
github-copilot103
cursor100
claude-code89
TanStack Query v5 完全指南:React 数据管理、乐观更新、离线支持
2,500 周安装
Angular编译器CLI (ngtsc) 架构详解:Ivy编译、模板类型检查与AOT
324 周安装
事件溯源(Event Sourcing)技术指南:实现审计追踪、时间点查询与CQRS架构
168 周安装
数据库索引策略指南:B-tree、Hash、GiST、BRIN索引优化查询性能与维护
164 周安装
Gradio:快速构建机器学习Web界面和交互式演示的Python库
171 周安装
Pencil转代码工具:一键将Pencil设计导出为React/Vue/HTML生产代码
172 周安装
just-scrape AI网页抓取CLI工具 - 智能数据提取与自动化爬虫
172 周安装
| Take control | self.clients.claim() in SW |