プログレッシブウェブアプリについて~デモアプリで効果を検証~
2016年 08月 03日
07/13(金)に社内の数名でGoogleforMobileに参加してきました
https://events.withgoogle.com/google-for-mobile/program/
多くのセッションがあり、全体としては先日のGoogleI/Oで発表されたFireBaseについての内容が多く、GoogleのFirebaseに対する強い意思を感じられました
この記事ではGoogleforMobileでも2セッション発表がありました、プログレッシブウェブアプリの紹介をします
プログレッシブウェブアプリについて
プログレッシブウェブアプリとはwebアプリとネイティブアプリ両方の良い部分を兼ね備えた、webアプリです
(教科書通りに実装すれば)特徴は下記の通りです
ホーム画面からの起動
ホーム画面に追加することで、あたかもネイティブアプリかのように起動できます
Push
ネイティブアプリのプッシュ通知をwebアプリに対して実現できます
ネットワーク接続に依存しない
ServiceWorkerのキャッシュを活用することにより、オフラインやネット環境が良くない場所でも高速で動作します
レスポンシブでアプリ感覚
AppShellモデルに基いて実装されるため、アプリ感覚で操作することができます
リンクで共有
あくまでもWebアプリなのでURLを使って簡単に共有でき、インストールの必要がありません
わざわざ教科書通りに実装すればと書いたのは、必要な部分だけを実装することが可能で、例えばホーム画面に追加したときの動作をリッチにしたいのでmanifest.jsonだけ登録するということができます
この必要な部分だけを実装できるのもプログレッシブウェブアプリのいいところだと個人的には思います
実装はmanifest.jsonとServiceWorkerを活用します
ServiceWorkerについて
バックグラウンドで実行するJavaScriptです
普段のJavaScriptとは違い、DOMに直接アクセスすることはできません
ただキャッシュやpushメッセージなど様々な機能を持ち合わせています
逆にさまざまなことができてしまうので、セキュリティ面からHTTPSでしか動作しません
ですのでプログレッシブウェブアプリもHTTPSで通信することが前提となります
今回はHome画面への追加とキャッシュについて、コードも交えながら、ご紹介します
Home画面からの起動をリッチにする
manifest.jsonを記述し、読み込んであげるだけで実現できますコンテンツからの呼び出し
link rel="manifest" href="manifest.json
manifest.json
{
"name":"ExciteProgressiveWebApp",
"short_name":"Excite",
"icons":[{
"src":"https://s.eximg.jp/exnews/a/img/apple-touch-icon.png",
"sizes":"150x150",
"type":"image/png"
}],
"start_url":"/index.html"
}
nameとshort_nameの使い分けはHome画面に追加した際のアイコン下に表示されるのがshort_name
スプラッシュ画面等に表示されるのがnameです
上記に加えて、下記を設定しておくと、よりネイティブアプリっぽくなります
"display":"standalone",
"background_color":"#111111",
"theme_color":"#111111",
displayは[fullscreen|standalone|minimal-ui|browser]の4つのモードがあり、standaloneを設定しておくと、chromeのタブではなく独立して起動してくれるので、ネイティブアプリと差がなくなり、オススメです
manifest.jsonの詳しい仕様はこちら
https://www.w3.org/TR/appmanifest/
キャッシュを活用し、ネットワーク接続に依存しないサービスにする
実装については下記Google公式のページを参考にしておりますhttps://codelabs.developers.google.com/codelabs/your-first-pwapp-ja/index.html
ServiceWorkerの登録
キャッシュの登録、呼び出しにはServiceWorkerを活用しますまずはブラウザにServiceWorkerを登録します
app.js(通常のコンテンツ側のjs)
if('serviceWorker' in navigator){
navigator.serviceWorker.register('/service-worker.js').then(function (registration){
console.log('ServiceWorker registration successful with scope:', registration.scope);
}).catch(function (err){
console.log('ServiceWorker registration failed:',err);
});
}
ポイントはservice-worker.jsの場所ですここで読み込んだ位置の配下がServiceWorkerの有効スコープになるため,よく検討する必要があります
ServiceWorkerは4つのイベントを受け取ることができます
それぞれのイベントに対して、適したキャッシュの処理を実装していきます
イベント名 | 発火タイミング |
---|---|
install | serviceWorker.registerが成功した直後 |
activate | install後またはServiceWorkerが更新され、ページに対してコントール可能になった際 |
fetch | ページ内でネットワークリクエストが発生した際 |
message | メッセージを受け取った際 |
キャッシュの書き込み
ServiceWorkerがインストールされたタイミングで静的コンテンツをキャッシュしておきますservice-worker.js
var files = [
'/styles.css',
'/images/lep.png',
'/images/E1468374024477_1.jpg',
'/images/E1468542105874_1.jpg',
'/images/E1468631912161_1.jpg',
'/images/E1466132241582_1.jpg',
'/images/E1466132241582_2.jpg'
];
self.addEventListener('install', function(e) {
console.log('[ServiceWorker] Install');
e.waitUntil(
caches.open_(cacheVersion).then(function(cache) {
console.log('[ServiceWorker] Caching app shell');
return cache.addAll(files);
}));
});
caches.openでキャッシュストレージを開き,addALLにキャッシュしたいファイルを一覧を渡して、完了です注意としては1つでもキャッシュ生成に失敗すると、全ての生成が失敗します
キャッシュの更新(削除)
activateになったタイミングで前回のキャッシュversionと比べて、変更があれば古いキャッシュを削除します
service-worker.js
self.addEventListener('activate',function(e) {
console.log('[ServiceWorker] Activate');
e.waitUntil(
caches.keys().then(function(keyList) {
return Promise.all(keyList.map(function(key) {
console.log('[ServiceWorker] Removing old cache',key);
if(key !== cacheVersion){
return caches.delete(key);
}
}));
})
);
});
caches.keysは現存する全てのkeyを配列で返却します戦略的にkeyを分けている場合の削除処理はご注意ください
キャッシュからの読み込み
service-worker.js
self.addEventListener('fetch',function(e){
console.log('[ServiceWorker]Fetch',e.request.url);
e.respondWith(
caches.match(e.request).then(function(response){
return response | | fetch(e.request);
})
);
});
caches.matchでcache内を探し、あればそれを返却し、なければfetchメソッドで探しにいきます厳格に実装するのであればfetchメソッドの結果も確認しておくべきでしょう
これでキャッシュの追加、読み込み、更新ができるようになりました
キャッシュの効果を確認する
数値でみる


300KB程度の画像ですが、通信が発生しないので処理時間は1/30程度に減少しているのが、確認できます!
オフラインでの動作
実装は簡単ですが、威力は絶大です!
参考にしているGoogle公式チュートリアルにもありますが、
このまま実際のサービスに登録するのは危険です
キャッシュ戦略を考え、適したものに書き換えてください
まとめ
webアプリとネイティブアプリの差はどんどん小さくなっていくと思いますプログレッシブウェブアプリは今後も様々なサービスで対応がすすみ、webアプリはネイティブアプリに近づいていくでしょう
ネイティブアプリはインストールや共有のしづらさがwebアプリに劣っていましたが、Instant Appsの登場でインストール、共有がURL1つで行えるようになりました
(Instant Appsをネイティブアプリとして捉えるかどうかは議論の余地があります)
これからは各サービスに適した方法を選択し、なにをユーザーに届けるかが、より大事になっていくのでないかと思います
参考
https://codelabs.developers.google.com/codelabs/your-first-pwapp-ja/index.htmlhttps://www.w3.org/TR/appmanifest/
https://www.w3.org/TR/service-workers/
エンジニア募集
詳しくは、こちらの採用情報ページをご覧ください。
一緒にプログレッシブウェブアプリのサービス導入を目指しましょう!