宇都宮佑亮

「改めまして、Progressive Web Appsと申します」── Web UXの新たな基準を考える

Progressive Web Appsというワードが世に出て約2年半が経ちました。2015年10月に開催されたChrome Dev SummitにてFlipkartの事例をもってお披露目となったそのコンセプトは、2018年現在までに徐々に成功事例を増やしながらWeb界隈の注目を集め、ついに先日(忘れもしない2018年3月30日!)iOS 11.3からiOSデバイスでも一部の機能が利用できるようになるまで成長しました。これは、まるで進学する我が子を見ているかのような、新年度にふさわしい晴れやかなニュースですし、いい機会なので PWAとは何かを改めて振り返ってみようと思います。

pwa01

Webに足りなかったもの

私はWebが大好きです。リンクを1つクリックしたら(インストールなど煩わしい手続きなしで)すぐに新しいコンテンツを読めるのは最高の体験だなと常日頃感じています。ただし、今までのWebアプリのユーザー体験は決して最適なものではないとも思っています。たとえば以下のような体験をした覚えはありませんか?

  • 朝の満員電車でも情報収集に勤しむ現代のビジネスパーソンのあなた。つり革に捕まりながらなんらかの記事コンテンツを見ようとリンクをタップする
  • 途端にHTMLのレスポンス待ちで真っ白な画面。レスポンスが返ってきたと思ったら今度はWebアプリ側のローディングGIFが動き出す。
  • しばらく待つとテキストが表示され読み始めるものの、時間差で画像が差し込まれレイアウトがずれる(どこを読んでいたんだっけ?)。極めつけは全画面広告が画面を占拠し、嫌になってそっとブラウザを閉じる(代わりにソーシャルアプリやゲームアプリを起動する)。

このように世に出回っている多くのWebアプリはパフォーマンスに課題があります。特に、さまざまなスペックなデバイスがあらゆる環境で利用されるモバイルにおいては、Webアプリは単純に耐えられないくらい遅く、実測で3G回線と同等な環境における平均ページロード時間は19秒もかかっているという統計もあります[1]。 Webアプリはとにかくこのパフォーマンスをどうにかしなければなりません。

一方ですでにネイティブ アプリに慣れ親しんでいるユーザーの期待値を満たすためには、パフォーマンスを改善するだけでは十分ではありません。

  • ホーム画面にアイコンを追加したり
  • プッシュ通知がタイミングよく送られてきたり
  • オフラインでも動作する信頼性だったり

「ユーザー体験の基準」がどんどん上がっていくなか、Webアプリだけがこのような体験を提供できずに取り残されている状況がずっと続いていました。時代に合わせて、ユーザーが求める機能性もWebアプリで実現できなければならないのです。

PWAはベストプラクティス集である

そこで満を持して登場したのがPWAです。PWAと聞くと何かを特定の技術を指すものと思われるかもしれませんが、実はそのコンセプトは幅広く、どちらかというと前述した Webアプリ本来の課題を解決し、よりよいユーザー体験を実現するためのベストプラクティス集のようなものです。Googleが提供しているProgressive Web App Checklist なるものがあり詳細な内容はそちらを確認いただければと思いますが、PWAのコンセプトをざっくり申し上げると以下となります。

  • とにかくパフォーマンスいいWebアプリを作りましょう。SPAでもSPAじゃなくても、どんなフレームワーク使っても使わなくても、なんでもいいので、表示が速くて画面遷移がスムーズなものにしてユーザーに好かれましょう。
  • また、最新のWeb APIを積極的に使って、機能性も身につけましょう。たとえば、Webアプリをホーム画面に追加できるようにしたり、オフラインでも動作するようにしたり。そんな今までWebアプリでは実現できなかった機能が実装できるようになったんだから、やらない手はないでしょう?

少し上記でも触れていますが、PWAはあくまでベストプラクティス集なので特定のサードパーティーフレームワークやライブラリには依存しません。世の中には ReactやAngularを使ったPWAもありますし、Vanilla JavaScriptなPWAもあります。また、サーバーサイドの構成には一切言及しません。したがって技術スタックを変えずとも適用可能なベストプラクティスでありますし、一度に全部を適用するのではなく段階的に(= Progressively)最適化することもできます。

以下の項目でPWAの特徴をより具体的に見ていきます。

ハイパフォーマンス

何より重要であり最も難しいテーマとなります。パフォーマンスの基準としては、2015年にChromeチームが発表したユーザー中心に考えるパフォーマンスモデルであるRAIL[2]を前提に考えていただければ間違いありませんが、各Webアプリの特性や環境に応じてそれぞれのサービスごとに Performance Budget(ロード時間やファイルサイズ等の上限を決め、それを「予算」として管理する)を設けることをオススメします。たとえば、2017年のChrome Dev SummitでChromeチームが推奨したPerfromance Budgetは、TTI(Time to interactive)を5秒以内、それを実現するためにクリティカルパスのファイルサイズを170KB以内に収めるというものです。[3][4]

何事もまずは現状分析から。Webアプリのパフォーマンス測定やプロファイリングをするLighthouseChrome DevTools等のツールを使って現状の評価とボトルネックを確認し、対応すべきポイントを決めて最適化を進めます。その後はCalibreSpeedCurveといったパフォーマンスモニタリングツールを利用して定点観測するといいでしょう。

pwa02

ハイパフォーマンスなPWAを作るためのデザインパターンであるPRPLパターンもまた参考にすべき方式です。PRPLパターンはPush 、Render、Pre-cache、Lazy-loadの頭文字を取ったもので、主にApp Shell モデルやSPAを採用したWebアプリのために用意されたパターンですが、それ以外の構成に関してもパフォーマンス向上のヒントとなる点が多くあります。

  • Push/Render: 初期描画に必須なリソースをHTTP/2 Server Pushするか link rel=preload を利用することで先行取得します。そしてそのリソースを利用し、特にAbove the fold [5] (スクロールしないで見える範囲)を優先してレンダリングします。
  • Pre-cache: 次ページ(またはルート)で使用するであろうリソースを先読みします。これにはService Workerを利用するといいでしょう。
  • Lazy-load: 初期描画に必要なもの以外はすべてLazy-loadします。SPAであれば別ルートの取得がこちら該当しますし、スクロールに合わせたフラグメントの取得であればIntersection Observer APIを利用するといいでしょう。

上記のとおり、Webアプリでもパフォーマンスをあげるためのツールやデザインパターンなど、使えるナレッジが多くたまってきました。ぜひともこれらを利用してハイパフォーマンスなWebアプリの開発を実現してください。

ホーム画面に追加

Web App Manifestを実装するとWebアプリをホーム画面に追加できるようになります。以下のようなJSON形式のマニフェストファイルを用意し、

link タグでページにひも付けた上で、Service Workerをインストールすると、

このようにホーム画面へ追加を促すプロンプトが上がってきます。ホームに追加した後はフルスクリーンで起動しますし、Androidの場合は1つの アプリとしてみなされディープリンクまで可能です。

pwa03

1点補足すると、この「ホーム画面に追加」は強力ですが、よりユーザーに気持ちよく追加してもらえるように、意味のあるタイミングでプロンプトを表示するとさらにいいでしょう。これを実現するためにぜひともonbeforeinstallpromptイベントハンドラーを利用してください。

onbeforeinstallpromptイベントハンドラーはプロンプトが表示される直前に呼び出されます。そのタイミングでプロンプト表示が適切でなければ表示をキャンセルし、別の任意のタイミングで表示することができます。たとえば、画面上に独自の「ホームに追加アイコン」を作成し、それの onclickのイベントハンドラ内でプロンプトを表示させることも可能です。

また執筆時点での最新のChrome Canary (67.0.3394.0)ではプロンプト表示形式を以下のように変更して、ホームに追加の体験をより良くすべく機能検証をしています。ポイントはページ下部に表示されるバナーがコンテンツの邪魔にならないサイズに調整され、PWAが追加済であればそのディープリンク用のバナーに差し替えます。また、前述のprompt関数を利用してユーザーのインタラクションに応じてプロンプトを表示する際は、モーダル表示に切り替わります。

pwa04

オフライン対応

ハイパフォーマンスの解説でも少し登場したService WorkerCache APIを利用することでWebアプリをオフラインで実行することが可能になります。Service Workerとはオリジン単位(またはパスの粒度)でインストール可能なJavaScriptで書かれたクライントサイドプロキシのことで、ページ内で発生するHTTPリクエスト / レスポンスを監視したり、それを書き換えたり、必要に応じてリソースを先読みすることもできます。これとCache APIというHTTPリクエスト/ レスポンスオブジェクトをKey-Value形式で保存できるAPIを組み合わせることで、オフライン時でもあらかじめキャッシュしておいたレスポンスを利用すことができるようになるわけです。

pwa05

このService WorkerとCache APIの組み合わせは「Service Worker の紹介」に記載の方法で使用することができますが、より汎用的な使い方をまとめたWorkboxというライブラリを利用するのも1つの手です。Workbox を利用すると実装が煩雑になる、特定のファイルのラインタイムキャッシュも以下のようにシンプルに記述することができます。

Workboxはその他にも、ファイル単位でリビジョンを付与したPrecachingExpirationの定義Background Syncを利用したオフライン時の Google Analytics の計測などの機能も提供します。

オフラインの戦略はWebアプリの特性に応じて変わってくるものです。例えばニュースサイトであれば、トップページに出ている注目記事をすべて先読みし、オフラインでも読めるようにすることも考えられますし、ECサイトであればまた別の戦略が適している場合もあるでしょう。電波状況が比較的良好な日本においても、速度制限や電車内などオフライン(または不安定なネットワーク)になりえるユースケースは存在しますし、最低限でも Offline Dinasourはユーザーに見せないようにフォールバックページを用意すべきです。

その他のWeb API

以下にPWAを実装する上で注目すべきその他のWeb APIを列挙します。

上記のAPIも組み合わせることによって、リエンゲージメント性の向上や商品の購買フローを最適化することができます。

クロスプラットフォームなイニシアチブ

上記にあげたPWAで利用するWeb APIは、すべてのブラウザで利用できることを目標に標準化を進めています。個別の対応状況を確認するにはCan I Useをご参照いただければと思いますが、冒頭に述べましたとおりiOSでもiOS 11.3からSafariでService Workerが動作するようになりましたし、Microsoft はWindowsストアにPWAを載せはじめました[6]。このように、さまざまな形でPWAのクロスプラットフォーム利用の機運が高まっていると言えます。

また、PWAはProgressive Enhancementな実装を推奨している点も改めて触れておきます。たとえばService Workerを利用したオフラインキャッシュの実装は、Service Workerが未実装なブラウザでもWebアプリ本来の動作には影響がないように以下のように条件分岐をいれることを推奨しています。

特定の環境のみでしか動かないWebアプリを作るのではなく、どの環境でもハイパフォーマンスに動作するようにし、最新のWeb APIが利用できる環境では進んでそれをレバレッジするとよいでしょう。

Webアプリ vs ネイティブ アプリ

PWAはその機能性や実現したいコンセプトから「PWAはネイティブ アプリを潰そうとしているのか?」「Webアプリがネイティブアプリにかなうはずない!」など当該テーマにおいては辛辣なコメントを見かける場面もあります。ここで1つお伝えしたいことは、多くのユーザーはネイティブアプリなのかWebアプリなのかというプラットフォームの選択には関心がないということです。大事なのは「うまい、やすい、はやい」ユーザー体験の良いサービスを提供することです。PWAの出現によりWebアプリでもそれを実現する準備が整いつつあり、事業者やソフトウェア エンジニアとしては理想のサービスを提供できるプラットフォームのオプションが増えるという意味で、このWebの進化を前向きに捉えていただければ幸いです。

実際Webアプリにはまだできないこともあります[7]。ただし、Webアプリには他のプラットフォームよりも優れたリーチがあり、その意味でWeb アプリとネイティブ アプリがむしろ手を取ることは、戦略としても決して矛盾しません。

pwa06

使用したい機能がWebプラットフォームが提供するもので十分であり、技術スタックをWebのみに集約するメリットも感じるのであれば「PWA のみ」という選択肢も今後は出てくるかと思います。しかし、前述した「まだできないこともある」という前提を加味するとネイティブアプリが活躍する場面は引き続きありますし、PWAは「Webアプリ vs ネイティブアプリ」という二者択一を迫る提案ではない点をご理解ください。

ユーザー体験の新たな基準

このようにPWAはWebアプリの「ユーザー体験の新たな基準」を満たすために作られたベストプラクティスでしかありません。重要なのはそれぞれの機能を単純に実装するのではなく、Webアプリの特性に応じて使い分け、ときにはカスタマイズし、ユーザーによりよいサービスを提供することです。今回ご紹介しました各種ツールやデザインパターン、iOSでもService Workerが利用できるようになった点など、舞台は整いつつあります。ぜひとも最高のWeb体験を実現すべくPWAの考え方を取り入れてみてください。

'; js_seriesContent.className = "js_seriesContent"; js_seriesContent.innerHTML = js_seriestitle.innerHTML; js_seriesContent.appendChild(js_serieslist_ul); if ( js_parent.lastChild == js_superior ) { js_parent.appendChild(js_seriesContent); } else { js_parent.insertBefore(js_seriesContent, js_superior.nextSibling); } if (js_serieslist_li_length > 5) { document.getElementsByClassName('moveToSeriesTop')[0].style.display = 'block'; document.getElementsByClassName('moveToSeriesTop')[0].href = document.getElementsByClassName('seriesmeta')[0].getElementsByTagName('a')[0].href; } })(this, this.document); // ソーシャルボタンをクリックされたらgaに送信 var elements, i; elements = document.querySelectorAll('.sns-buttons > li > a.facebook-btn-icon-link'); for (i = 0; i < elements.length; i++) { elements[i].addEventListener('click', function() { ga('send', 'social', 'Facebook', 'like', '/uskay/25391/'); }, false); } elements = document.querySelectorAll('.sns-buttons > li > a.twitter-btn-icon-link'); for (i = 0; i < elements.length; i++) { elements[i].addEventListener('click', function() { ga('send', 'social', 'Twitter', 'tweet', '/uskay/25391/'); }, false); } elements = document.querySelectorAll('.sns-buttons > li > a.google-plus-btn-icon'); for (i = 0; i < elements.length; i++) { elements[i].addEventListener('click', function() { ga('send', 'social', 'Google+', '+1', '/uskay/25391/'); }, false); } elements = document.querySelectorAll('.sns-buttons > li > a.hatena-btn-icon'); for (i = 0; i < elements.length; i++) { elements[i].addEventListener('click', function() { ga('send', 'social', 'Hatebu', 'bookmark', '/uskay/25391/'); }, false); } elements = document.querySelectorAll('.sns-buttons > li > a.pocket-btn-icon'); for (i = 0; i < elements.length; i++) { elements[i].addEventListener('click', function() { ga('send', 'social', 'Pocket', 'bookmark', '/uskay/25391/'); }, false); }

週間PVランキング

新着記事

Powered byNTT Communications

tag list

アクセシビリティ イベント エンタープライズ デザイン ハイブリッド パフォーマンス ブラウザ プログラミング マークアップ モバイル 海外 高速化 Angular2 AngularJS Chrome Cordova CSS de:code ECMAScript Edge Firefox Google Google I/O 2014 HTML5 Conference 2013 html5j IoT JavaScript Microsoft Node.js Polymer Progressive Web Apps React Safari SkyWay TypeScript UI UX W3C W3C仕様 Webアプリ Web Components WebGL WebRTC WebSocket WebVR