2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【最小構成】PWAをオフライン対応にする手順(manifest + Service Worker だけで動かす)

2
Posted at

【最小構成】PWAをオフライン対応にする手順(manifest + Service Worker だけで動かす)

はじめに

PWA(Progressive Web App)は、Webサイトをスマートフォンのホーム画面に追加して、アプリのように使えるようにする仕組みです。アプリストアの審査が要らず、Webの技術だけで作れます。

ライブラリを入れる前に、まず「何が最低限必要なのか」を押さえておくと、あとでトラブルが起きたときに原因を追いやすくなります。この記事では、manifest と Service Worker の2つだけで、ホーム画面に追加でき、オフラインでも開けるところまでを作ります。

対象読者

  • HTML / CSS / JavaScript の基本が分かる方
  • PWAの仕組みを、フレームワークなしで一度確認しておきたい方

完成イメージ

  • スマホのホーム画面にアイコンを追加できる
  • 追加したアイコンから、ブラウザのバーなしで起動できる
  • 通信を切っても、表示済みのページが開ける

PWAに必要なもの

必要なもの 役割
HTTPS Service Worker はHTTPSでのみ動く(localhost は例外)
manifest.json アプリ名・アイコン・表示方法を伝える
Service Worker キャッシュを管理し、オフライン時の応答を返す

ファイル構成

/
├─ index.html
├─ offline.html
├─ manifest.json
├─ sw.js
└─ icons/
   ├─ icon-192.png
   └─ icon-512.png

アイコンは 192×192 と 512×512 の PNG を用意します。

1. manifest.json を作る

{
  "name": "サンプルPWA",
  "short_name": "サンプル",
  "start_url": "/",
  "scope": "/",
  "display": "standalone",
  "background_color": "#ffffff",
  "theme_color": "#0e7c86",
  "icons": [
    { "src": "/icons/icon-192.png", "sizes": "192x192", "type": "image/png" },
    { "src": "/icons/icon-512.png", "sizes": "512x512", "type": "image/png" }
  ]
}
  • display: "standalone" にすると、ブラウザのアドレスバーが消えてアプリらしい見た目になります。
  • start_url は、ホーム画面のアイコンから開いたときの最初のページです。

2. index.html で読み込む

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>サンプルPWA</title>
  <link rel="manifest" href="/manifest.json">
  <meta name="theme-color" content="#0e7c86">
  <link rel="apple-touch-icon" href="/icons/icon-192.png">
</head>
<body>
  <h1>サンプルPWA</h1>
  <p>オフラインでも開けます。</p>

  <script>
    if ('serviceWorker' in navigator) {
      window.addEventListener('load', () => {
        navigator.serviceWorker.register('/sw.js')
          .then((reg) => console.log('SW registered:', reg.scope))
          .catch((err) => console.error('SW registration failed:', err));
      });
    }
  </script>
</body>
</html>

iOS(Safari)では、apple-touch-icon を指定しておくとホーム画面のアイコンに使われます。

3. offline.html を用意する

通信できず、キャッシュにもないページを開いたときに出す画面です。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>オフライン</title>
</head>
<body>
  <h1>オフラインです</h1>
  <p>通信が回復したら、もう一度お試しください。</p>
</body>
</html>

4. sw.js を書く

const CACHE_NAME = 'app-cache-v1';
const PRECACHE = [
  '/',
  '/index.html',
  '/offline.html',
  '/manifest.json',
  '/icons/icon-192.png',
  '/icons/icon-512.png',
];

// インストール時に、必要なファイルをキャッシュする
self.addEventListener('install', (event) => {
  event.waitUntil(
    caches.open(CACHE_NAME).then((cache) => cache.addAll(PRECACHE))
  );
  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) => {
  const { request } = event;
  if (request.method !== 'GET') return;

  // ページ遷移:ネットワーク優先、失敗したらキャッシュ → offline.html
  if (request.mode === 'navigate') {
    event.respondWith(
      fetch(request).catch(async () => {
        const cached = await caches.match(request);
        return cached || caches.match('/offline.html');
      })
    );
    return;
  }

  // それ以外(CSS・画像など):キャッシュ優先、なければ取得して保存
  event.respondWith(
    caches.match(request).then((cached) => {
      if (cached) return cached;
      return fetch(request).then((response) => {
        const copy = response.clone();
        caches.open(CACHE_NAME).then((cache) => cache.put(request, copy));
        return response;
      });
    })
  );
});

キャッシュ戦略の考え方

対象 戦略 理由
HTML(ページ遷移) ネットワーク優先 常に新しい内容を見せたい
CSS・画像・JS キャッシュ優先 速く表示でき、オフラインでも使える

すべてをキャッシュ優先にすると、更新しても古い画面が出続けるという問題が起きます。ページの性質に合わせて使い分けるのがポイントです。

5. 動作を確認する

  1. HTTPS、または localhost でサイトを開く
  2. Chromeの開発者ツール → Application タブを開く
  3. Manifest に情報が出ていること、Service Workers に activated と出ていることを確認する
  4. Service Workers の Offline にチェックを入れて、再読み込みしてもページが表示されることを確認する
  5. Lighthouse タブで PWA の項目を確認する

ホーム画面への追加方法

  • Android(Chrome):メニューから「ホーム画面に追加」または「アプリをインストール」
  • iOS(Safari):共有ボタンから「ホーム画面に追加」

つまずきやすい点

  • 更新が反映されない:sw.js を変えたら CACHE_NAME のバージョンも上げます。古いキャッシュが残っていると、前の画面が出続けます。
  • sw.js の置き場所:Service Worker は、置いた場所より下の範囲しか制御できません。基本はサイトのルート直下に置きます。
  • 登録に失敗する:HTTPSでないと動きません。デプロイ先がHTTPSになっているか確認します。
  • パスの間違い:PRECACHE に存在しないファイルが1つでもあると、addAll 全体が失敗します。

まとめ

  • PWAの最小構成は、HTTPS・manifest・Service Worker の3つです。
  • HTMLはネットワーク優先、静的ファイルはキャッシュ優先が基本です。
  • 仕組みが分かったあとは、Workbox や Vite のPWAプラグインを使うと、キャッシュ管理が楽になります。

参考


ホームページ制作・PWA開発を承っています。
SE Web / PWA Studio:https://majestic-crostata-aff623.netlify.app/

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?