PWAを実際に作ってみて学ぶ

最近ブラウザはOSになるのかとかそういった話題に耳がピクついています、町田です。今回はその入りとしてPWAについて調べて作ってみたいと思います。
PWAとは
Progressive Web Appsの略で、2018年6月にChromeに正式に取り入れられました。また、特徴ははじめてのプログレッシブ ウェブアプリ | Web | Google Developersに以下のように記されています。
プログレッシブ ウェブアプリには以下の特徴があります。
• 段階的 – プログレッシブ・エンハンスメントを基本理念としたアプリであるため、 ブラウザに関係なく、すべてのユーザーに利用してもらえます。
• レスポンシブ – パソコンでもモバイルでもタブレットでも、次世代の端末でも、 あらゆるフォームファクタに適合します。
• ネットワーク接続に依存しない – Service Worker の活用により、オフラインでも、 ネットワーク環境が良くない場所でも動作します。
• アプリ感覚 – App Shell モデルに基づいて作られているため、アプリ感覚で操作できます。
• 常に最新 – Service Worker の更新プロセスにより、常に最新の状態に保たれます。
• 安全 – 覗き見やコンテンツの改ざんを防ぐため、HTTPS 経由で配信されます。
• 発見しやすい – W3C のマニフェストとService Worker の登録スコープにより、 「アプリケーション」として認識されつつ、検索エンジンからも発見することができます。
• 再エンゲージメント可能 – プッシュ通知のような機能を通じで容易に 再エンゲージメントを促すことができます。
• インストール可能 – ユーザーが気に入ればアプリのリンクをホーム画面に残して おくことができ、アプリストアで探し回る必要はありません。
• リンク可能 – URL を使って簡単に共有でき、複雑なインストールの必要はありません。
また、PWAの必要な条件ついてはProgressive Web App Checklist | Web | Google Developersを参照してください。
これらの条件を満たすアプリを作るには、レスポンシブデザインやServiceWorker、AppShell、再エンゲージメント可能、インストール可能などを知る必要があります。今回は、PWAを作成することでこれらを利用し、理解を深めることを目指します。
実際に作ってみる
Progressive Web App tutorial – learn to build a PWA from scratchこちらの動画を参考に、NewsAPIを利用したニュースアプリを作成しました。完成品はこちらの動画の説明欄にあるDemoまたは私のリポジトリを確認ください。それでは、実際に作成したアプリから数箇所抜き出して見てみていきます。
manifest.json
manifest.json
はPWAに必須です。また、ブラウザに通知するため、index.html
のhead内に以下の一文を追加しています。
<link rel="manifest" href="manifest.json">
あまり見慣れないこのファイルですが、ウェブアプリ マニフェスト | Web | Google Developersによると
ウェブアプリ マニフェストはシンプルな JSON 形式のファイルです。デベロッパーはこのファイルを使用することで、ユーザーが想定するネイティブ アプリの場所(モバイル端末のホーム画面など)にウェブアプリやサイトを表示する方法を制御し、ユーザーが起動できる対象や起動時の外観を指定することができます。
ウェブアプリ マニフェストによって、サイトのブックマークを端末のホーム画面に保存することができます。この操作は、サイトが次のように起動された場合に可能です。
固有のアイコンと名前があり、その他のサイトと区別できる。
リソースのダウンロード中、またはキャッシュからの復元中に、ユーザーに表示するコンテンツがある。
サイトのリソースが利用可能になったときに唐突に画面遷移することのないように、ブラウザに既定の表示特性を指定している。
このすべてを、テキスト ファイルのメタデータによるシンプルな仕組みによって実現するのが、ウェブアプリ マニフェストです。
と書いています。アプリとして起動された際の起動待機画面や、アプリのアイコンなどを設定できるようです。更に詳しくはWeb App Manifest | MDNを参照してください。
次に、このアプリで使用されているmanifest.json
の中身についての一部抜き出して簡単な説明を加えました。
"name": "News", // インストール時のバナーに表示される
"short_name": "News", // インストール後のアプリのラベルに表示される
"background_color": "#ffffff", // アプリ起動時、初回レンダリングまでのバックグラウンドカラー
"display": "browser", // ブラウザUIを表示する。'standalone'にするとUI非表示になる。
"start_url": "/pwa-from-scratch", // アプリを開いたときにアクセスするページ
となっています。
ServiceWorkerにsw.jsを登録
app.js
の15行目でwindowのloadイベントに合わせて行っています。以下に抜粋してみます。
// ServiceWorkerに対応しているブラウザか判定
if('serviceWorker' in navigator) {
try {
// sw.jsをサービスワーカーに登録
navigator.serviceWorker.register('sw.js');
console.log(`SW registered`);
} catch (error) {
console.log(`SW not registered`);
}
}
これで登録できました。
ところでServiceWorkerって?
オフライン対応や、通知などを支える基盤技術です、詳しくはService Worker の紹介 | Web | Google Developersに書かれています。
sw.js
前項でServiceWorkerに登録したjsファイルです。中身を覗いて何を行っている確認してみます。各コードにコメントを付与しました。
// ChacheStorageに登録する名前
const cacheName = 'news-v1';
// キャッシュしておきたいstaticAssets
const staticAssets = [
'./',
'./style.css',
'./app.js',
'./fallback.json',
'./images/fetch-dog.jpg'
];
// ServiceWorkerへの登録が済んだ際に発行される'install'イベントへの紐付け
self.addEventListener('install', async () => {
// cachesはCacheStorageのクライアント
// データがあってもなくても開き、使用する準備をします。
const cache = await caches.open(cacheName);
// キャッシュしておきたいAssetsを追加します。
cache.addAll(staticAssets);
});
// fetch()が呼ばれた際のイベントへの紐付けを行います。
self.addEventListener('fetch', e => {
const req = e.request;
const url = new URL(req.url);
// アクセスが自身のページか、外部ページかどうか判定しています。
if (url.origin === location.origin) {
// 自身のページならばキャッシュしたstaticAssetsから取得できるか試す。
e.respondWith(cacheFirst(req));
} else {
// 外部ページならばHTTPのリクエストを行い、失敗したらキャッシュ、またはエラーページを出す。
e.respondWith(networkFirst(req));
}
});
// キャッシュ>リクエストのレスポンスの順で取得する。
// 今回は主に自身のstaticAssetsの取得に使用
async function cacheFirst(req) {
const cachedResponse = await caches.match(req);
return cachedResponse || fetch(req);
}
// リクエストのレスポンス>キャッシュ>エラーデータの順で取得する。
// 今回は外部ページへのリクエストの制御に使用しています。
async function networkFirst(req) {
const cache = await caches.open('news-dynamic');
try {
const res = await fetch(req);
cache.put(req, res.clone());
return res;
} catch (error) {
const cachedResponse = await cache.match(req);
return cachedResponse || await caches.match('./fallback.json');
}
}
という感じでオフライン対応の処理を追加しています。
以上で要所は紹介できたと思います。次は実際にショートカットを追加してPWAを使ってみます。
デスクトップに追加する
使用したPCはMac、ブラウザはChrome(version 69)を使用しています。
まずURL欄にchrome://flags
と入力します。
次に、以下の4つの項目をEnabledにします。
そうしたらChromeを再起動します。
起動したら今回作成したデモページを開きます。
Chrome右上の︙
ボタンからショートカットを作成
をクリックします。すると確認ダイアログが開き、作成
をクリックすると、ショートカットが追加されたフォルダが表示されます。
これで追加は完了しました。MacであればSpotlightなどでNewsと入力してみてください、おそらくアプリとして認識されているはずです。
終わりに
PWAの概要の確認から実際に作成、使用までやってみることで、大枠を理解できました。ネイティブアプリの完全代替までは行かなくとも、プラウザがあれば使える軽快なアプリ 開発の発展に今後も期待していきたいです。