mcpField
P
pwa-development

pwa-development

github.com/mindrally開発289785 回インストール3 週間前に更新
リポジトリ

pwa-development について

pwa-development は、オフライン優先の Progressive Web Apps を構築するためのガイドと例をまとめたスキルです。マニフェスト、Service Worker、キャッシュ、同期、通知、パフォーマンス、テストを扱います。

主な機能

  1. 01オフライン優先設計の指針
  2. 02マニフェストと Service Worker の設定
  3. 03キャッシュ戦略の例
  4. 04バックグラウンド同期とプッシュ通知
  5. 05パフォーマンスとテストの推奨事項

活用シーン

  1. 01オフライン対応 PWA の計画
  2. 02静的・動的コンテンツのキャッシュ戦略選定
  3. 03バックグラウンド同期や通知の追加
  4. 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 経由で配信します
  • 意味のあるオフラインエクスペリエンスを提供します
  • サービスワーカーの更新を適切に処理します
  • 適切なエラー処理を実装します
  • 読み込み状態とスケルトンを追加します

コメント