はじめに
既存サイトをPWA化しServiceWorkerでキャッシュのコントロールとプッシュ通知を受け取れるプロトタイプを作ったのでそれについてまとめました。
Firebaseを利用したプッシュ通知の配信基盤も作ったのでそれについても後日書きます。
前提
NuxtやGatsbyなどのフレームワークを使っていないプロジェクトへの導入です。
目標
以下の実装をゴールとします。
この記事では上の3つまでをゴールとして進めていきます。
- 既存サイトをPWA化する
- Workboxを使ってキャッシュをコントロールする
- オフラインで閲覧できるようにする
- プッシュ通知を受信できるようにする
- プッシュ通知の配信基盤をFirebaseで構築する
PWA化する
まず既存サイトをPWA化していきます。
PWAとは
- 「Progressive Web Apps」の略
- ネイティブアプリのようなUXを提供する技術
- ホーム画面へ追加するとプッシュ通知を受け取れる
- オフラインでも動作する
- スプラッシュスクリーンを設定したりフルスクリーンで表示などが可能
- ストアを介さずにインストール可能
詳しくは以下を参照してください。
はじめてのプログレッシブウェブアプリ | Web Fundamentals | Google Developers
前提としてやること
-
manifest.json
を用意してhead内で読み込む - Service Workerを設定・登録する
- Service Workerは段階的に機能を追加していくことが可能
- SSL対応
- Service WorkerはHTTPSを介して提供されるページでしか登録できない
- アイコン用画像の用意
- アイコン画像が読み込まれない状態だとService Workerが有効にならないので注意
manifest.json
アプリケーションの情報(名前、説明、アイコン)やカラーや初期表示ページのURL等を記述し設定するjsonファイルです。
詳しくは以下を参照してください。
ウェブアプリマニフェスト | MDN
導入する
サイトのルートディレクトリにmanifest.json
を作成します。
{
"name": "pwa_prototype",
"short_name": "pwa_prototype",
"description": "PWAのプロトタイプサイト",
"start_url": "/",
"display": "standalone",
"background_color": "#000000",
"theme_color": "#4DBA87",
"icons": [
{
"src": "/img/icons/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/img/icons/android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"gcm_sender_id": "103953800507"
}
設定した属性の内容は次の通りです。
属性 | 説明 |
---|---|
name | アプリケーションの名前 |
short_name | 短縮表示用のアプリケーションの名前 |
description | アプリケーションの説明 |
start_url | アプリケーションを起動したときに読み込むURL |
display | 表示モードを定義 |
background_color | スプラッシュ画面の背景色を指定 |
theme_color | アプリケーションの既定のテーマ色 |
icons | アプリケーションのアイコンとして機能する画像ファイルの配列を指定 |
gcm_sender_id | FCMを利用する場合、値は103953800507 で固定 |
Google Analytics用のパラメータを埋め込みたい場合は、start_url
で指定する。
また、Firebase Cloud Messaging(FCM)を利用する場合は、gcm_sender_id
の値は103953800507
で固定になるので注意。
この他にも設定できる項目があるので、詳しくは以下を参照してください。
ウェブアプリマニフェスト | MDN
manifest.json
を作成したらheadタグで以下のように読み込みます。
<link rel="manifest" href="manifest.json">
手で書くのがめんどくさかったらこんな便利なものもある。
参考
ウェブアプリマニフェスト | MDN
Webアプリマニフェスト| Webの基礎| Google Developers
PWAのmanifest.jsonとiconsの各サイズのアイコン画像を自動生成してくれるApp Manifest Generatorの紹介
「ホーム画面に追加」を表示するには
- 訪問回数が2回以上あり、訪問の間隔が5分以上ある
- 適切に設定された(
short_name
またはname
、icons
、start_url
、display
を含む)manifest.json
が読み込まれている - Service Workerに
fetch
イベントを記述する(中身は空でも良い)
self.addEventListener('fetch', function(e) {
// 中身は空でもOK
})
この程度の単純な作業で済みます。デメリットも特になさそうなので、とりあえず実装しておきます。
参考
Add to Home Screen | Web Fundamentals | Google Developers
Service Workerの準備をする
以下を実現するために準備をしていきます。
- Workboxを使ってキャッシュをコントロールする
- オフラインで閲覧できるようにする
Service Workerとは
- Webページとは別にバックグラウンドで実行するJavaScript
- キャッシュ管理、プッシュ通知、バックグラウンド同期などを行えるようにする基盤技術
-
HTTPS
を介して提供されるページかlocalhost
でしか登録できない
詳しくは以下を参照してください。
Service Worker の紹介 | Web Fundamentals | Google Developers
Service Workerの登録
サイトのルートディレクトリにsw.js
を作成します。
作成したら下記のようにService Workerとして登録します。
<script>
if ('serviceWorker' in navigator) {
window.addEventListener('load', function () {
navigator.serviceWorker.register('./sw.js', { scope: './' })
.then(function (registration) {
console.log('SW.js registration successful with scope: ', registration.scope);
}, function (err) {
console.log('Sw.js registration failed: ', err);
});
});
}
</script>
Workboxによるキャッシュのコントロール
それでは実際にsw.js
にキャッシュのコントロールの処理を実装していきます。
// workboxを読み込む
importScripts('https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-sw.js');
// Service Workerライフサイクルをスキップする
workbox.core.skipWaiting();
workbox.core.clientsClaim();
// preload
workbox.navigationPreload.enable();
// Pre cache - Service Workerのインストール時に指定したファイルをキャッシュする
// https://developers.google.com/web/tools/workbox/modules/workbox-precaching
workbox.precaching.precacheAndRoute([
"/css/style.css",
"/js/main.js",
"/js/libs/slick.min.js",
// リビジョンをつけることも可能
{ url: '/index.html', revision: 'abcd1234' },
]);
// Runtime cache - アクセスしたタイミングで取得したファイルをキャッシュする
// https://developers.google.com/web/tools/workbox/modules/workbox-routing
// font
workbox.routing.registerRoute(
({event}) => event.request.destination === 'font',
new workbox.strategies.StaleWhileRevalidate({
cacheName: 'font',
plugins: [
new workbox.expiration.Plugin({
maxEntries: 50,
maxAgeSeconds: 7 * 24 * 60 * 60,
}),
],
})
);
// image
workbox.routing.registerRoute(
({event}) => event.request.destination === 'image',
new workbox.strategies.StaleWhileRevalidate({
cacheName: 'image',
plugins: [
new workbox.expiration.Plugin({
maxEntries: 50,
maxAgeSeconds: 7 * 24 * 60 * 60,
}),
],
})
);
// js
workbox.routing.registerRoute(
({event}) => event.request.destination === 'script',
new workbox.strategies.StaleWhileRevalidate({
cacheName: 'js',
plugins: [
new workbox.expiration.Plugin({
maxEntries: 50,
maxAgeSeconds: 7 * 24 * 60 * 60,
}),
],
})
);
// css
workbox.routing.registerRoute(
({event}) => event.request.destination === 'style',
new workbox.strategies.StaleWhileRevalidate({
cacheName: 'css',
plugins: [
new workbox.expiration.Plugin({
maxEntries: 50,
maxAgeSeconds: 7 * 24 * 60 * 60,
}),
],
})
);
// ページごと
workbox.routing.registerRoute(
new RegExp('/item/'),
new workbox.strategies.StaleWhileRevalidate({
networkTimeoutSeconds: 3,
cacheName: 'itempage',
plugins: [
new workbox.expiration.Plugin({
maxEntries: 50,
}),
],
})
);
self.addEventListener('install', function (event) {
// empty
});
self.addEventListener('activate', function (event) {
// empty
});
// インストールバナーを表示する
self.addEventListener('fetch', function (event) {
// empty
})
キャッシュ戦略
キャッシュ戦略(サービスワーカーのレスポンスの返し方)を設定するために、Workbox Strategies
を使います。
以下のようなパターンがあるので、それぞれ用途によって使い分ける必要があります。
- Stale-While-Revalidate
- キャッシュを優先して返す。キャッシュがなければネットワークから返す。さらにネットワークへリクエストしてキャッシュを更新する。
- Cache First
- キャッシュを優先して返す。キャッシュがあればネットワークへリクエストしない。キャッシュはない場合、ネットワークから返す。
- Network First
- ネットワークから優先して返す。失敗した場合はキャッシュを返す。
- Network Only
- ネットワークからのみ返す。
- Cache Only
- キャッシュのみ返す。
今回の場合は、基本的にStaleWhileRevalidate
で設定しています。
詳しくは以下を参照。
Workbox Strategies | Google Developers
PWA: Workbox Strategies 和訳
キャッシュができているか確認する
chromeのデベロッパーツールのApplicationタブのCache Storageを確認。
ちゃんとキャッシュできていることが確認できます。
cacheName
で指定した名前にファイルが格納されています。
リロードしてNetworkタブを開くと、Service Workerからキャッシュを返せていることが確認できます。
ネットワークをoffline状態にしてリロードをしても、Service Workerからキャッシュを返せています。
ここまでのまとめ
これでPWA化とServiceWorkerを登録してWorkboxでキャッシュのコントロールをすることまでが実現できました。
この記事では基本的なキャッシュの処理しかしていませんが、要件によっては処理が複雑になることもあると思います。
とりあえず既存サイトをPWA化させることができました。