Service Worker(オフライン対応)

serviceWorker.register cache fetch イベント

Service WorkerはWebページとネットワークの間に位置するプロキシです。オフライン対応・キャッシュ・プッシュ通知などPWAの中核技術です。

このブラウザの Service Worker 状態

確認中...

Service Worker のライフサイクル

register()installinginstalled/waitingactivatingactivated

コード例:Service Worker の登録

js
// メインスクリプト(main.js)

// Service Worker の登録
if ('serviceWorker' in navigator) {
  window.addEventListener('load', async () => {
    try {
      const reg = await navigator.serviceWorker.register('/sw.js', {
        scope: '/',  // 管理するパスの範囲
      });
      console.log('登録成功:', reg.scope);

      // アップデート確認
      reg.update();

      // 新しいSWが待機中のとき
      reg.addEventListener('updatefound', () => {
        const newWorker = reg.installing;
        newWorker.addEventListener('statechange', () => {
          if (newWorker.state === 'installed' && navigator.serviceWorker.controller) {
            // 新バージョンが利用可能
            showUpdateBanner();
          }
        });
      });
    } catch (err) {
      console.error('登録失敗:', err);
    }
  });
}

// 登録解除
const reg = await navigator.serviceWorker.getRegistration();
await reg.unregister();

コード例:Service Worker 本体(sw.js)

js
// sw.js(Service Worker ファイル)
const CACHE_NAME = 'my-cache-v1';
const URLS_TO_CACHE = ['/', '/app.js', '/styles.css', '/offline.html'];

// インストール時:リソースをキャッシュ
self.addEventListener('install', (e) => {
  e.waitUntil(
    caches.open(CACHE_NAME).then(cache => cache.addAll(URLS_TO_CACHE))
  );
  self.skipWaiting(); // すぐにアクティブにする
});

// アクティベート時:古いキャッシュを削除
self.addEventListener('activate', (e) => {
  e.waitUntil(
    caches.keys().then(keys =>
      Promise.all(keys.filter(k => k !== CACHE_NAME).map(k => caches.delete(k)))
    )
  );
  self.clients.claim(); // 既存のページも管理下に
});

// フェッチ時:キャッシュ優先(Cache First)
self.addEventListener('fetch', (e) => {
  e.respondWith(
    caches.match(e.request).then(cached => {
      if (cached) return cached; // キャッシュがあれば返す
      return fetch(e.request)    // なければネットワーク
        .then(res => {
          const clone = res.clone();
          caches.open(CACHE_NAME).then(c => c.put(e.request, clone));
          return res;
        })
        .catch(() => caches.match('/offline.html')); // オフライン時
    })
  );
});

キャッシュ戦略の比較

戦略動作適した用途
Cache Firstキャッシュ→なければNetwork静的アセット(CSS/JS/画像)
Network FirstNetwork→失敗したらCacheAPI・動的コンテンツ
Stale While RevalidateCacheを返しつつ、バックグラウンドで更新頻繁に更新されるコンテンツ
Cache OnlyCacheのみ(Networkなし)完全オフラインアプリ

NEWWorkbox(Google製 Service Worker ライブラリ)& Background Sync

Service Workerの実装を簡略化するGoogleのライブラリです。Next.js/Viteとの統合も容易です。

js
// Workbox(npm install workbox-window workbox-precaching)
import { Workbox } from 'workbox-window';
const wb = new Workbox('/sw.js');
wb.register();

// sw.js(Workbox製)
import { precacheAndRoute } from 'workbox-precaching';
import { registerRoute } from 'workbox-routing';
import { CacheFirst, NetworkFirst } from 'workbox-strategies';

precacheAndRoute(self.__WB_MANIFEST); // ビルド時に自動生成

registerRoute(
  ({ request }) => request.destination === 'image',
  new CacheFirst({ cacheName: 'images' })
);

// Background Sync(オフライン時の送信をキューイング)
// 接続が回復したら自動的に送信
import { BackgroundSyncPlugin } from 'workbox-background-sync';
registerRoute(
  ({ url }) => url.pathname.startsWith('/api/'),
  new NetworkOnly({ plugins: [new BackgroundSyncPlugin('apiQueue')] }),
  'POST'
);

🤖 AIプロンプトテンプレート

以下のようなService Workerの登録・キャッシュ・オフライン対応のサンプルコードを生成してください:
- navigator.serviceWorker.register()でSWを登録する基本実装
- installイベントでcaches.addAll()を使った事前キャッシュの実装
- fetchイベントでCache FirstとNetwork Firstのキャッシュ戦略を切り替える方法
- activateイベントで古いキャッシュバージョンを削除するパターン
- updatefoundイベントで新バージョン検知時にユーザーに通知する実装

⚠️ このプロンプトはあくまでたたき台です。AIの回答はモデルやバージョン、会話の文脈によって毎回異なります。意図通りに動かない場合は、条件を追記・修正してお使いください。