TechHub

エンジニアの成長をサポートする技術情報サイト

← 記事一覧に戻る

Progressive Web Apps(PWA)とは?実装方法とベストプラクティス

公開日: 2024年3月19日 著者: mogura
Progressive Web Apps(PWA)とは?実装方法とベストプラクティス

疑問

Progressive Web Apps(PWA)とは何で、どのように実装するのでしょうか?ネイティブアプリのような体験をWebで実現する方法を一緒に学んでいきましょう。

導入

Progressive Web Apps(PWA)は、Web技術を使ってネイティブアプリのような体験を提供するアプリケーションです。オフライン機能、プッシュ通知、ホーム画面への追加など、従来のWebアプリを超えた機能を実現できます。

本記事では、PWAの基本概念から、Service Worker、マニフェストファイル、オフライン機能の実装方法まで、実践的なコード例とともに詳しく解説します。

PWAのイメージ

解説

1. Progressive Web Apps(PWA)とは

Progressive Web Apps(PWA)は、Web技術(HTML、CSS、JavaScript)を使って構築されたアプリケーションで、ネイティブアプリのような体験を提供します。Service Workerによるオフライン機能、マニフェストファイルによるアプリライクな設定、プッシュ通知による再エンゲージメントなど、従来のWebアプリを超えた機能を実現できます。

PWAの特徴

1. プログレッシブ: すべてのブラウザで動作し、機能が段階的に向上
2. レスポンシブ: あらゆるデバイスで動作
3. 接続に依存しない: Service Workerでオフライン機能を実現
4. アプリライク: マニフェストファイルでアプリのような体験
5. 常に最新: Service Workerで自動更新
6. 安全: HTTPSで配信
7. 検索可能: SEOに最適化
8. 再エンゲージ可能: プッシュ通知でユーザーを再エンゲージ
9. インストール可能: ホーム画面に追加可能
10. リンク可能: URLで共有可能

2. PWAの要件

PWAとして認識されるには、いくつかの必須要件を満たす必要があります。HTTPSでの配信、マニフェストファイルの提供、Service Workerの実装などが基本的な要件です。これらの要件を満たすことで、ブラウザがPWAとして認識し、インストール可能なアプリとして扱われます。

必須要件

1. HTTPS: セキュアな接続(localhostは除く)
2. マニフェストファイル: manifest.jsonまたはmanifest.webmanifest
3. Service Worker: オフライン機能とキャッシング
4. レスポンシブデザイン: モバイルとデスクトップで動作
5. アイコン: 様々なサイズのアイコン(192x192、512x512など)

推奨要件

- オフライン機能
- プッシュ通知
- アニメーションとトランジション
- 高速な読み込み

3. マニフェストファイルの作成

マニフェストファイル(manifest.json)は、PWAのメタデータを定義するJSONファイルです。アプリの名前、アイコン、表示モード、テーマカラー、スタートURLなどを定義します。このファイルにより、ブラウザはアプリをインストール可能なアプリとして認識し、ホーム画面に追加する際の情報を取得します。

基本的なマニフェストファイル

基本的なマニフェストファイルの例:

{
  "name": "My PWA",
  "short_name": "PWA",
  "description": "Progressive Web Appの説明",
  "start_url": "/",
  "display": "standalone",
  "background_color": "#ffffff",
  "theme_color": "#000000",
  "orientation": "portrait",
  "icons": [
    {
      "src": "/icons/icon-192x192.png",
      "sizes": "192x192",
      "type": "image/png"
    },
    {
      "src": "/icons/icon-512x512.png",
      "sizes": "512x512",
      "type": "image/png"
    }
  ]
}


主要なプロパティ:
- name: アプリの完全な名前
- short_name: ホーム画面に表示される短い名前
- start_url: アプリ起動時のURL
- display: 表示モード(standalone、fullscreen、minimal-ui、browser)
- icons: 様々なサイズのアイコン

HTMLでのマニフェストの読み込み

HTMLでマニフェストファイルを読み込む:

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta name="theme-color" content="#000000">
  <link rel="manifest" href="/manifest.json">
  <title>My PWA</title>
</head>
<body>
  <!-- アプリのコンテンツ -->
</body>
</html>


重要なポイント:
- <link rel="manifest">でマニフェストを読み込む
- theme-colorメタタグでテーマカラーを設定
- viewportメタタグでレスポンシブデザインを有効化

displayモード

displayモードの種類:

standalone: ネイティブアプリのような表示
- ブラウザのUIを非表示
- アプリのように動作
- 推奨されるモード

fullscreen: フルスクリーン表示
- すべてのUIを非表示
- ゲームやメディアアプリに適している

minimal-ui: 最小限のUI
- 最小限のブラウザUIを表示
- iOS Safariで使用

browser: 通常のブラウザ表示
- 完全なブラウザUIを表示
- PWAとしてのメリットが少ない

4. Service Workerの実装

Service Workerは、ブラウザとサーバーの間で動作するプロキシサーバーです。バックグラウンドで動作し、ネットワークリクエストをインターセプトして、キャッシュから応答を返すことができます。これにより、オフライン機能や高速な読み込みを実現できます。

Service Workerの登録

Service Workerの登録:

if ('serviceWorker' in navigator) {
  window.addEventListener('load', () => {
    navigator.serviceWorker.register('/sw.js')
      .then(registration => {
        console.log('Service Worker登録成功:', registration.scope);
      })
      .catch(error => {
        console.log('Service Worker登録失敗:', error);
      });
  });
}


登録のポイント:
- serviceWorkerのサポートを確認
- ページ読み込み後に登録
- エラーハンドリングを実装
- 登録の成功/失敗をログに記録

基本的なService Worker

基本的なService Workerの実装:

インストールイベント: キャッシュの初期化
- 必要なリソースをキャッシュに保存
- キャッシュのバージョン管理

フェッチイベント: リクエストのインターセプト
- キャッシュから応答を返す
- ネットワークから取得してキャッシュに保存
- オフライン時のフォールバック

アクティベートイベント: 古いキャッシュの削除
- 新しいService Workerが有効化されたとき
- 古いキャッシュを削除

キャッシュ戦略

キャッシュ戦略の種類:

Cache First: キャッシュを優先
- オフライン対応が重要
- 静的リソースに適している
- 古いデータが表示される可能性

Network First: ネットワークを優先
- 最新データが重要
- 動的コンテンツに適している
- オフライン時はキャッシュから取得

Stale While Revalidate: キャッシュを返しつつ更新
- 高速な応答と最新データの両立
- バックグラウンドで更新
- バランスの取れた戦略

Network Only: ネットワークのみ
- 常に最新データが必要
- キャッシュしない

Cache Only: キャッシュのみ
- 完全にオフライン
- 事前にキャッシュが必要

5. Service Workerの更新

Service Workerは、ファイルが変更されると自動的に更新を検出します。しかし、更新を適切に処理しないと、ユーザーが古いバージョンを使い続ける可能性があります。更新の検出、新しいService Workerの有効化、ユーザーへの通知などを適切に実装することで、スムーズなアップデートを実現できます。

更新の実装

Service Workerの更新フロー:

1. 更新の検出: ブラウザが新しいService Workerを検出
2. インストール: 新しいService Workerがインストールされる
3. 待機: 古いService Workerが制御している間は待機
4. アクティベート: ページが閉じられるか、skipWaiting()で即座に有効化
5. 古いキャッシュの削除: アクティベート時に古いキャッシュを削除

skipWaiting(): 即座に新しいService Workerを有効化
- インストール時に呼び出す
- ユーザーに通知してから有効化することを推奨

更新の検出

更新の検出方法:

registration.updatefound: 更新が見つかったときのイベント

registration.addEventListener('updatefound', () => {
  const newWorker = registration.installing;
  newWorker.addEventListener('statechange', () => {
    if (newWorker.state === 'installed' && navigator.serviceWorker.controller) {
      // 新しいService Workerが利用可能
      showUpdateNotification();
    }
  });
});


定期的なチェック: 定期的に更新をチェック
setInterval(() => {
  registration.update();
}, 60000); // 1分ごと


ページ読み込み時のチェック: ページ読み込み時に更新をチェック

6. オフライン機能の実装

オフライン機能は、PWAの重要な特徴の一つです。Service Workerを使用してリソースをキャッシュし、ネットワーク接続がない場合でもアプリを使用できるようにします。オフライン状態の検出、オフラインページの表示、オフライン時の動作を適切に実装することで、優れたユーザー体験を提供できます。

オフライン検出

オフライン状態の検出:

navigator.onLine: オンライン/オフライン状態の確認

if (navigator.onLine) {
  console.log('オンライン');
} else {
  console.log('オフライン');
}


online/offlineイベント: 接続状態の変化を検出
window.addEventListener('online', () => {
  console.log('オンラインになりました');
});

window.addEventListener('offline', () => {
  console.log('オフラインになりました');
});


実際の接続確認: navigator.onLineは信頼できない場合があるため、実際にリクエストを送信して確認

オフラインページ

オフラインページの実装:

オフラインページの作成: ネットワークエラー時に表示
- シンプルなHTMLページ
- オフラインであることを伝えるメッセージ
- 再試行ボタン

Service Workerでの処理:
- ネットワークリクエストが失敗したとき
- キャッシュにもない場合
- オフラインページを返す

実装例:

self.addEventListener('fetch', event => {
  event.respondWith(
    fetch(event.request)
      .catch(() => {
        return caches.match(event.request)
          .then(response => {
            if (!response) {
              return caches.match('/offline.html');
            }
            return response;
          });
      })
  );
});

7. プッシュ通知

プッシュ通知は、サーバーからユーザーにメッセージを送信する機能です。ユーザーがアプリを使用していないときでも、重要な情報や更新を通知できます。通知の許可リクエスト、Service Workerでの通知表示、プッシュメッセージの受信などを実装します。

通知の許可をリクエスト

通知の許可をリクエスト:

Notification.requestPermission(): 通知の許可をリクエスト

if ('Notification' in window) {
  Notification.requestPermission().then(permission => {
    if (permission === 'granted') {
      console.log('通知が許可されました');
    }
  });
}


許可状態の確認:
- granted: 許可されている
- denied: 拒否されている
- default: まだ確認していない

ベストプラクティス:
- ユーザーが通知を必要とするタイミングでリクエスト
- 許可の理由を説明
- 一度拒否されたら再度リクエストしない

Service Workerでの通知表示

Service Workerでの通知表示:

self.registration.showNotification(): Service Workerから通知を表示

self.registration.showNotification('タイトル', {
  body: '通知の本文',
  icon: '/icon.png',
  badge: '/badge.png',
  tag: 'notification-tag',
  data: { url: '/page' }
});


通知オプション:
- body: 通知の本文
- icon: アイコン画像
- badge: バッジ画像
- tag: 通知のタグ(同じタグの通知は置き換え)
- data: 通知に関連するデータ
- actions: アクションボタン
- requireInteraction: ユーザーが閉じるまで表示

通知クリックイベント: 通知をクリックしたときの処理

8. インストールプロンプト

PWAは、ブラウザが自動的にインストール可能と判断した場合、インストールプロンプトを表示します。しかし、カスタムのインストールプロンプトを実装することで、ユーザーにインストールを促すことができます。インストール可能な状態の検出、カスタムプロンプトの表示、インストールの処理などを実装します。

インストール可能な状態の検出

インストール可能な状態の検出:

beforeinstallpromptイベント: インストールプロンプトが表示される前に発火

let deferredPrompt;

window.addEventListener('beforeinstallprompt', (e) => {
  e.preventDefault();
  deferredPrompt = e;
  showInstallButton();
});


インストールボタンの表示:
- beforeinstallpromptイベントが発火したらボタンを表示
- ユーザーがインストールできる状態であることを示す

インストールの実行:
deferredPrompt.prompt();
deferredPrompt.userChoice.then((choiceResult) => {
  if (choiceResult.outcome === 'accepted') {
    console.log('インストールが承認されました');
  }
  deferredPrompt = null;
});

9. PWAのテスト

PWAのテストは、様々なデバイスとブラウザで動作を確認することが重要です。Lighthouseを使用した自動テスト、手動での動作確認、オフライン機能のテストなど、包括的なテストを実施することで、優れたPWAを構築できます。

Lighthouseでのテスト

LighthouseでのPWAテスト:

Lighthouse: Chrome DevToolsに統合されたテストツール
- PWAスコアの測定
- 必須要件の確認
- 推奨事項の確認

テスト項目:
- HTTPSの使用
- マニフェストファイルの存在
- Service Workerの登録
- レスポンシブデザイン
- アイコンの提供
- オフライン機能
- インストール可能性

スコア:
- 90-100: 優秀
- 50-89: 改善の余地あり
- 0-49: 大幅な改善が必要

実行方法:
- Chrome DevToolsのLighthouseタブ
- コマンドライン: lighthouse https://example.com

手動テスト

手動テストのチェックリスト:

インストール:
- [ ] インストールプロンプトが表示される
- [ ] インストールが成功する
- [ ] ホーム画面にアイコンが表示される
- [ ] スタンドアロンモードで起動する

オフライン機能:
- [ ] オフライン時にキャッシュから読み込める
- [ ] オフラインページが表示される
- [ ] オンライン復帰時に動作する

Service Worker:
- [ ] Service Workerが登録される
- [ ] キャッシュが機能する
- [ ] 更新が正しく動作する

プッシュ通知:
- [ ] 通知の許可がリクエストされる
- [ ] 通知が表示される
- [ ] 通知をクリックしてアプリが開く

10. ベストプラクティス

PWAを効果的に実装するには、いくつかのベストプラクティスに従うことが重要です。適切なキャッシュ戦略の選択、オフライン機能の実装、パフォーマンスの最適化、ユーザー体験の向上など、様々な観点から最適化を行います。

キャッシュ戦略の選択

キャッシュ戦略の選択:

- 静的リソース: Cache First戦略
- 動的コンテンツ: Network First戦略
- APIレスポンス: Stale While Revalidate戦略
- 画像: Cache First + 遅延読み込み

キャッシュのバージョン管理:
- キャッシュ名にバージョンを含める
- 更新時に古いキャッシュを削除
- キャッシュサイズを監視

パフォーマンスの最適化

パフォーマンスの最適化:

- 初期読み込み: クリティカルリソースの優先読み込み
- コード分割: 必要なコードのみを読み込む
- 画像最適化: WebP、AVIFの使用
- フォント最適化: font-display: swapの使用
- Service Worker: バックグラウンドでのキャッシング

ユーザー体験の向上

ユーザー体験の向上:

- オフラインインジケーター: オフライン状態を明確に表示
- スプラッシュスクリーン: アプリ起動時の表示
- スムーズなトランジション: ページ遷移のアニメーション
- エラーハンドリング: 適切なエラーメッセージ
- インストールプロンプト: 適切なタイミングで表示

セキュリティ

セキュリティのベストプラクティス:

- HTTPS: すべての通信を暗号化
- Content Security Policy: XSS攻撃の防止
- Service Workerのスコープ: 適切なスコープの設定
- マニフェストの検証: マニフェストファイルの妥当性確認

まとめ

Progressive Web Apps(PWA)は、Web技術を使ってネイティブアプリのような体験を提供するアプリケーションです。Service Workerによるオフライン機能、マニフェストファイルによるアプリライクな体験、プッシュ通知による再エンゲージメントなど、様々な機能を実現できます。

PWAを実装するには、HTTPS、マニフェストファイル、Service Workerが必須です。適切なキャッシュ戦略を選択し、オフライン機能を実装することで、ユーザーに優れた体験を提供できます。

実践的なプロジェクトでPWAを実装し、様々なデバイスとブラウザでテストすることで、より良いPWAを構築できるようになります。

Webセキュリティのベストプラクティスとは?実践的な対策ガイド Webパフォーマンス最適化とは?実践的なテクニックとツール