pwa-development について
pwa-development は、オフライン優先の Progressive Web Apps を構築するためのガイドと例をまとめたスキルです。マニフェスト、Service Worker、キャッシュ、同期、通知、パフォーマンス、テストを扱います。
主な機能
- 01オフライン優先設計の指針
- 02マニフェストと Service Worker の設定
- 03キャッシュ戦略の例
- 04バックグラウンド同期とプッシュ通知
- 05パフォーマンスとテストの推奨事項
活用シーン
- 01オフライン対応 PWA の計画
- 02静的・動的コンテンツのキャッシュ戦略選定
- 03バックグラウンド同期や通知の追加
- 04PWA の性能とオフライン動作の確認
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ mindrally/ skills --skill pwa-developmentインストール後、Claude Code で/pwa-developmentを入力して読み込まれたか確認します。
詳細
プログレッシブウェブアプリ開発ガイドライン
オフラインファースト機能を備えたプログレッシブウェブアプリケーションの構築に精通しています。
基本原則
- オフラインファーストのエクスペリエンスを設計します
- 適切なキャッシュ戦略を実装します
- 高速な読み込みとスムーズなパフォーマンスを確保します
- ウェブアプリマニフェストのベストプラクティスに従います
- ネイティブアプリのようなエクスペリエンスを提供します
ウェブアプリマニフェスト
{
"name": "My Progressive Web App",
"short_name": "MyPWA",
"description": "A description of your app",
"start_url": "/",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#000000",
"icons": [
{
"src": "/icons/icon-192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/icons/icon-512.png",
"sizes": "512x512",
"type": "image/png"
}
]
}
サービスワーカーの登録
// Register service worker
if ('serviceWorker' in navigator) {
window.addEventListener('load', async () => {
try {
const registration = await navigator.serviceWorker.register('/sw.js');
console.log('SW registered:', registration.scope);
} catch (error) {
console.error('SW registration failed:', error);
}
});
}
サービスワーカーの実装
// sw.js
const CACHE_NAME = 'v1';
const STATIC_ASSETS = [
'/',
'/index.html',
'/styles.css',
'/app.js',
'/offline.html'
];
// Install event - cache static assets
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_NAME).then((cache) => {
return cache.addAll(STATIC_ASSETS);
})
);
self.skipWaiting();
});
// Activate event - cleanup old caches
self.addEventListener('activate', (event) => {
event.waitUntil(
caches.keys().then((cacheNames) => {
return Promise.all(
cacheNames
.filter((name) => name !== CACHE_NAME)
.map((name) => caches.delete(name))
);
})
);
self.clients.claim();
});
// Fetch event - serve from cache or network
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request).then((response) => {
return response || fetch(event.request);
}).catch(() => {
return caches.match('/offline.html');
})
);
});
キャッシュ戦略
キャッシュ優先(静的アセット)
async function cacheFirst(request) {
const cached = await caches.match(request);
return cached || fetch(request);
}
ネットワーク優先(動的コンテンツ)
async function networkFirst(request) {
try {
const response = await fetch(request);
const cache = await caches.open(CACHE_NAME);
cache.put(request, response.clone());
return response;
} catch {
return caches.match(request);
}
}
古いキャッシュを返しながら再検証
async function staleWhileRevalidate(request) {
const cache = await caches.open(CACHE_NAME);
const cached = await cache.match(request);
const fetchPromise = fetch(request).then((response) => {
cache.put(request, response.clone());
return response;
});
return cached || fetchPromise;
}
バックグラウンド同期
// Register sync in main app
async function registerSync() {
const registration = await navigator.serviceWorker.ready;
await registration.sync.register('sync-data');
}
// Handle sync in service worker
self.addEventListener('sync', (event) => {
if (event.tag === 'sync-data') {
event.waitUntil(syncData());
}
});
async function syncData() {
const data = await getQueuedData();
await fetch('/api/sync', {
method: 'POST',
body: JSON.stringify(data)
});
}
プッシュ通知
// Request permission
async function requestNotificationPermission() {
const permission = await Notification.requestPermission();
if (permission === 'granted') {
const registration = await navigator.serviceWorker.ready;
const subscription = await registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: PUBLIC_VAPID_KEY
});
// Send subscription to server
}
}
// Handle push in service worker
self.addEventListener('push', (event) => {
const data = event.data.json();
event.waitUntil(
self.registration.showNotification(data.title, {
body: data.body,
icon: '/icons/icon-192.png'
})
);
});
オフライン検出
// Check online status
window.addEventListener('online', () => {
console.log('Back online');
syncPendingData();
});
window.addEventListener('offline', () => {
console.log('Offline mode');
showOfflineIndicator();
});
パフォーマンスの最適化
- アプリシェルアーキテクチャを実装します
- ルートとコンポーネントに遅延読み込みを使用します
- レスポンシブ形式で画像を最適化します
- JavaScript バンドルのサイズを最小限に抑えます
- コード分割を使用します
テスト
- オフライン機能をテストします
- キャッシュの動作を確認します
- さまざまなネットワーク環境でテストします
- マニフェストとアイコンを検証します
- PWA の監査には Lighthouse を使用します
ベストプラクティス
- HTTPS 経由で配信します
- 意味のあるオフラインエクスペリエンスを提供します
- サービスワーカーの更新を適切に処理します
- 適切なエラー処理を実装します
- 読み込み状態とスケルトンを追加します
コメント
Markdown 対応