この Codelab は、Google Developers トレーニング チームが開発した「プログレッシブ ウェブアプリの開発」トレーニング コースの一部です。Codelab を順番に進めると、このコースを最大限に活用できます。
コースの詳細については、プログレッシブ ウェブアプリの開発の概要をご覧ください。
はじめに
このラボでは、Lighthouse を使用して、プログレッシブ ウェブアプリ(PWA)の標準に準拠しているかどうかをウェブサイトで監査します。また、Service Worker API を使用してオフライン機能を追加します。
学習内容
- Lighthouse でサイトを監査する方法
- アプリケーションにオフライン機能を追加する方法
必要な予備知識
- 基本的な HTML、CSS、JavaScript
- ES2015 の Promise について理解している
必要なもの
- ターミナル/シェルにアクセスできるパソコン
- インターネット接続
- Chrome ブラウザ(Lighthouse を使用する場合)
- テキスト エディタ
- 省略可: Android デバイスの Chrome
GitHub から pwa-training-labs リポジトリをダウンロードするか、クローンを作成し、必要に応じて Node.js の LTS バージョンをインストールします。
offline-quickstart-lab/app/ ディレクトリに移動して、ローカル開発用サーバーを起動します。
cd offline-quickstart-lab/app npm install node server.js
サーバーは Ctrl-c でいつでも終了できます。
ブラウザを開き、localhost:8081/ に移動します。サイトはシンプルで静的なウェブページです。
注: ラボに干渉しないように、Service Worker の登録を解除し、localhost の Service Worker キャッシュをすべてクリアします。Chrome DevTools でこれを行うには、[アプリケーション] タブの [ストレージを消去] セクションで [サイトデータを消去] をクリックします。
任意のテキスト エディタで offline-quickstart-lab/app/ フォルダを開きます。app/ フォルダは、ラボを構築する場所です。
このフォルダには次のものが含まれています。
images/フォルダにサンプル画像が含まれていますstyles/main.cssはメインのスタイルシートです。index.htmlはサンプルサイトのメイン HTML ページです。package-lock.jsonとpackage.jsonはアプリの依存関係を追跡します(この場合の依存関係はローカル開発用サーバーのみです)。server.jsはテスト用のローカル開発用サーバーですservice-worker.jsはサービス ワーカー ファイルです(現在は空です)。
サイトに変更を加える前に、Lighthouse で監査を行い、改善できる点を確認しましょう。
(Chrome で)アプリに戻り、デベロッパー ツールの [監査] タブを開きます。Lighthouse アイコンと構成オプションが表示されます。[デバイス] で [モバイル] を選択し、[監査] をすべて選択し、[スロットリング] オプションのいずれかを選択し、[ストレージをクリア] を選択します。

[監査を実行] をクリックします。監査が完了するまでに少し時間がかかります。
説明
監査が完了すると、デベロッパー ツールにスコアを含むレポートが表示されます。次のようなスコアが表示されます(スコアはまったく同じではない可能性があります)。
注: Lighthouse のスコアは概算であり、環境(たとえば、開いているブラウザ ウィンドウの数が多い場合など)の影響を受ける可能性があります。スコアは、ここに示されているものと完全に一致しない場合があります。

[プログレッシブ ウェブアプリ] セクションは次のようになります。

レポートには、次の 5 つのカテゴリのスコアと指標が表示されます。
- プログレッシブ ウェブアプリ
- パフォーマンス
- ユーザー補助
- ベスト プラクティス
- SEO
ご覧のとおり、このアプリはプログレッシブ ウェブアプリ(PWA)のカテゴリでスコアが低くなっています。スコアを改善しましょう。
レポートの PWA セクションを確認して、不足しているものを確認してください。
Service Worker を登録する
レポートに記載されている失敗の 1 つは、Service Worker が登録されていないことです。現在、app/service-worker.js に空のサービス ワーカー ファイルがあります。
index.html の末尾(終了タグ </body> の直前)に次のスクリプトを追加します。
<script>
if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker.register('service-worker.js')
.then(reg => {
console.log('Service worker registered! 😎', reg);
})
.catch(err => {
console.log('😥 Service worker registration failed: ', err);
});
});
}
</script>説明
このコードは、ページの読み込みが完了すると、空の service-worker.js サービス ワーカー ファイルを登録します。ただし、現在の Service Worker ファイルは空で、何も行いません。次のステップでサービスコードを追加しましょう。
リソースを事前にキャッシュに保存する
レポートに記載されているもう 1 つの失敗は、アプリがオフライン時にステータス コード 200 で応答しないことです。この問題を解決するには、サービス ワーカーを更新する必要があります。
サービス ワーカー ファイル(service-worker.js)に次のコードを追加します。
const cacheName = 'cache-v1';
const precacheResources = [
'/',
'index.html',
'styles/main.css',
'images/space1.jpg',
'images/space2.jpg',
'images/space3.jpg'
];
self.addEventListener('install', event => {
console.log('Service worker install event!');
event.waitUntil(
caches.open(cacheName)
.then(cache => {
return cache.addAll(precacheResources);
})
);
});
self.addEventListener('activate', event => {
console.log('Service worker activate event!');
});
self.addEventListener('fetch', event => {
console.log('Fetch intercepted for:', event.request.url);
event.respondWith(caches.match(event.request)
.then(cachedResponse => {
if (cachedResponse) {
return cachedResponse;
}
return fetch(event.request);
})
);
});ブラウザに戻り、サイトを更新します。コンソールで、サービス ワーカーが次のようになっていることを確認します。
- 登録済み
- インストール済み
- 有効
注: 以前にサービス ワーカーを登録したことがある場合や、すべてのイベントを発生させるのに問題がある場合は、サービス ワーカーを登録解除してページを更新します。それでも解決しない場合は、アプリのすべてのインスタンスを閉じてから、もう一度開きます。
次に、コマンドラインで Ctrl + c を実行して、ローカル開発サーバーを終了します。サイトを再度更新すると、サーバーがオフラインでも読み込まれることがわかります。
注: サービス ワーカーを取得できなかったことを示すコンソール エラー(An unknown error occurred when fetching the script. service-worker.js Failed to load resource: net::ERR_CONNECTION_REFUSED)が表示されることがあります。このエラーは、ブラウザがサービス ワーカー スクリプトを取得できなかった(サイトがオフラインであるため)ために表示されますが、サービス ワーカーを使用してサービス ワーカー自体をキャッシュに保存することはできないため、これは想定内です。そうしないと、ユーザーのブラウザは同じサービス ワーカーに永久に固定されてしまいます。
説明
index.html の登録スクリプトによってサービス ワーカーが登録されると、サービス ワーカーの install イベントが発生します。このイベントの間、install イベント リスナーは名前付きキャッシュを開き、cache.addAll メソッドで指定されたファイルをキャッシュに保存します。これは、通常ユーザーが初めてサイトにアクセスする install イベント中に発生するため、「プリキャッシュ」と呼ばれます。
Service Worker がインストールされた後、別の Service Worker が現在ページを制御していない場合、新しい Service Worker が「有効化」され(Service Worker で activate イベント リスナーがトリガーされます)、ページの制御を開始します。
有効なサービス ワーカーが制御するページからリソースがリクエストされると、リクエストはネットワーク プロキシのようにサービス ワーカーを通過します。リクエストごとに fetch イベントがトリガーされます。サービス ワーカーの fetch イベント リスナーは、キャッシュを検索し、キャッシュされたリソースが利用可能な場合は、そのリソースで応答します。リソースがキャッシュに保存されていない場合、リソースは通常どおりリクエストされます。
リソースをキャッシュに保存すると、ネットワーク リクエストを回避してアプリをオフラインで動作させることができます。これで、アプリはオフライン時にステータス コード 200 を返すことができます。
注: この例では、activate イベントはログイン以外の目的では使用されません。このイベントは、サービス ワーカーのライフサイクルに関する問題のデバッグに役立つように追加されました。
省略可: デベロッパー ツールの [アプリケーション] タブで [キャッシュ ストレージ] セクションを開くと、キャッシュに保存されたリソースを確認することもできます。

node server.js を指定して開発用サーバーを再起動し、サイトを更新します。次に、デベロッパー ツールの [監査] タブを再度開き、[新しい監査](左上のプラス記号)を選択して Lighthouse 監査を再実行します。監査が完了すると、PWA スコアが大幅に改善されたものの、まだ改善の余地があることがわかります。次のセクションでは、スコアの改善を続けます。
注: このセクションは省略可能です。ウェブアプリのインストール バナーのテストは、このラボの範囲外です。リモート デバッグを使用して、ご自身で試すことができます。
PWA スコアはまだ高くありません。レポートに記載されている残りの失敗の一部は、ユーザーにウェブアプリのインストールを求めるプロンプトが表示されないこと、アドレスバーにスプラッシュ画面やブランドカラーが設定されていないことです。これらの問題を解決し、追加の条件を満たすことで、ホーム画面に追加を段階的に実装できます。最も重要なのは、マニフェスト ファイルを作成することです。
マニフェスト ファイルを作成する
app/ に manifest.json というファイルを作成し、次のコードを追加します。
{
"name": "Space Missions",
"short_name": "Space Missions",
"lang": "en-US",
"start_url": "/index.html",
"display": "standalone",
"theme_color": "#FF9800",
"background_color": "#FF9800",
"icons": [
{
"src": "images/touch/icon-128x128.png",
"sizes": "128x128"
},
{
"src": "images/touch/icon-192x192.png",
"sizes": "192x192"
},
{
"src": "images/touch/icon-256x256.png",
"sizes": "256x256"
},
{
"src": "images/touch/icon-384x384.png",
"sizes": "384x384"
},
{
"src": "images/touch/icon-512x512.png",
"sizes": "512x512"
}
]
}マニフェストで参照されている画像は、アプリですでに提供されています。
次に、index.html の <head> タグの一番下に次の HTML を追加します。
<link rel="manifest" href="manifest.json">
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="application-name" content="Space Missions">
<meta name="apple-mobile-web-app-title" content="Space Missions">
<meta name="theme-color" content="#FF9800">
<meta name="msapplication-navbutton-color" content="#FF9800">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="msapplication-starturl" content="/index.html">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="icon" sizes="128x128" href="/images/touch/icon-128x128.png">
<link rel="apple-touch-icon" sizes="128x128" href="/images/touch/icon-128x128.png">
<link rel="icon" sizes="192x192" href="icon-192x192.png">
<link rel="apple-touch-icon" sizes="192x192" href="/images/touch/icon-192x192.png">
<link rel="icon" sizes="256x256" href="/images/touch/icon-256x256.png">
<link rel="apple-touch-icon" sizes="256x256" href="/images/touch/icon-256x256.png">
<link rel="icon" sizes="384x384" href="/images/touch/icon-384x384.png">
<link rel="apple-touch-icon" sizes="384x384" href="/images/touch/icon-384x384.png">
<link rel="icon" sizes="512x512" href="/images/touch/icon-512x512.png">
<link rel="apple-touch-icon" sizes="512x512" href="/images/touch/icon-512x512.png">サイトに戻ります。デベロッパー ツールの [Application] タブで、[Clear storage] セクションを選択し、[Clear site data] をクリックします。その後、ページを更新します。[マニフェスト] セクションを選択します。manifest.json ファイルで構成されているアイコンと構成オプションが表示されます。変更内容が表示されない場合は、シークレット ウィンドウでサイトを開いて再度確認してください。
説明
manifest.json ファイルは、ブラウザのクローム、ホーム画面のアイコン、スプラッシュ画面など、アプリのプログレッシブな側面の一部をスタイル設定してフォーマットする方法をブラウザに伝えます。また、ネイティブ アプリのように(つまり、ブラウザの外で)standalone モードで開くようにウェブアプリを設定するためにも使用できます。
この記事の執筆時点では、一部のブラウザでまだサポートが開発中であり、<meta> タグは、まだ完全にはサポートされていない特定のブラウザ向けに、これらの機能のサブセットを設定します。
index.html の古いキャッシュ バージョンを削除するために、サイトデータを消去する必要がありました(そのバージョンにはマニフェスト リンクがなかったため)。別の Lighthouse 監査を実行して、PWA スコアがどれだけ改善されたかを確認してください。
インストール メッセージを有効にする
アプリをインストールする次のステップは、インストール プロンプトをユーザーに表示することです。Chrome 67 ではユーザーに自動的にプロンプトが表示されましたが、Chrome 68 以降では、ユーザー操作に応じてプログラムでインストール プロンプトを有効にする必要があります。
次のコードを使用して、index.html の上部(<main> タグの直後)に「アプリをインストール」ボタンとバナーを追加します。
<section id="installBanner" class="banner">
<button id="installBtn">Install app</button>
</section>次に、次のスタイルを styles/main.css に追加して、バナーのスタイルを設定します。
.banner {
align-content: center;
display: none;
justify-content: center;
width: 100%;
}ファイルを保存します。最後に、次のスクリプトタグを index.html に追加します。
<script>
let deferredPrompt;
window.addEventListener('beforeinstallprompt', event => {
// Prevent Chrome 67 and earlier from automatically showing the prompt
event.preventDefault();
// Stash the event so it can be triggered later.
deferredPrompt = event;
// Attach the install prompt to a user gesture
document.querySelector('#installBtn').addEventListener('click', event => {
// Show the prompt
deferredPrompt.prompt();
// Wait for the user to respond to the prompt
deferredPrompt.userChoice
.then((choiceResult) => {
if (choiceResult.outcome === 'accepted') {
console.log('User accepted the A2HS prompt');
} else {
console.log('User dismissed the A2HS prompt');
}
deferredPrompt = null;
});
});
// Update UI notify the user they can add to home screen
document.querySelector('#installBanner').style.display = 'flex';
});
</script>ファイルを保存します。リモート デバッグを使用して、Android デバイスの Chrome でアプリを開きます。ページが読み込まれると、[アプリをインストール] ボタンが表示されます(パソコンでは表示されないため、モバイルでテストしてください)。ボタンをクリックすると、[ホーム画面に追加] のプロンプトが表示されます。手順に沿ってデバイスにアプリをインストールします。インストール後、新しく作成されたホーム画面アイコンをタップすると、ウェブアプリをスタンドアロン モード(ブラウザの外)で開くことができるようになります。
説明
HTML と CSS のコードは、ユーザーがインストール プロンプトを有効にできるようにするための非表示のバナーとボタンを追加します。
beforeinstallprompt イベントが発火すると、デフォルトのエクスペリエンス(Chrome 67 以前ではユーザーにインストールを自動的に促す)を防止し、グローバル deferredPrompt 変数で beforeinstallevent をキャプチャします。[アプリをインストール] ボタンは、beforeinstallevent の prompt() メソッドでプロンプトを表示するように構成されます。ユーザーが選択(インストールするかどうか)を行うと、userChoice プロミスはユーザーの選択(outcome)で解決されます。最後に、すべてが準備できたらインストール ボタンを表示します。
Lighthouse を使用してサイトを監査する方法と、オフライン機能の基本を実装する方法について説明しました。省略可能なセクションを完了した場合は、ウェブアプリをホーム画面にインストールする方法も学習しました。
その他のリソース
Lighthouse はオープンソースです。フォークして独自のテストを追加し、バグを報告できます。Lighthouse は、ビルドプロセスとの統合用のコマンドライン ツールとしても利用できます。
PWA トレーニング コースのすべての Codelab を確認するには、コースのウェルカム Codelab をご覧ください。