「Google I/O 2016」では、これからのWebアプリ開発パターンとして提唱しているPWApps(Progressive Web Apps)について多くのプレゼンテーションがなされました。
PWAppsとは、最新のWeb技術を有効に活用し、漸進的(Progressive)に高度なユーザー体験を提供しようとする概念です。このPWAppsの概念を具体化する一つの手法として、「PRPL」(パープル)と名付けられた開発・提供パターンが提案されました。本稿ではこのPRPLを解説するとともに、その有効性や課題点を考察します。
本稿は、Google I/O 2016の二つのセッションに関する、解説記事です。
- Polymer and Progressive Web Apps: Building on the modern web
- Progressive, Performant, Polymer: Pick Three
PRPLの概要、PWAppsとの関係
PRPLは、次の4つの頭文字をとった、開発パターンです。
- Push
- Render
- Pre-cache
- Lazy-load
モバイルWebアプリケーションにおいて、現行のWeb開発・提供パターンが抱える課題点を解決するものとしてGoogleが今回の I/Oで提案した新たなフレームワークパターンです。
Web Componentsや、Service Worker、HTTP/2 Serveer Pushといった Webの最新技術をフルに活用し、レスポンス性の高いユーザー体験を提供しようというものです。PWAppsの具体的なパターンの一つとして位置付けられます。
PRPLについては、以降のパラグラフで詳しく解説するとして、まず最初にPWAppsについて概説します。
PWAppsが最初に発表されたのは、昨年11月。シリコンバレーGoogleオフィスで開催された Chrome Dev Summitのことです。モバイルWebアプリにメインターゲットをおき、ネイティブアプリに近いユーザー体験(オフライン動作や高速なレスポンス性)を漸進的に提供していこうという考え方です。
ここで、漸進的といっても、イマイチピンと来ない方も多いことでしょう。具体例としてService Workerのパターンについて紹介します。Service Workerの詳細ついては、HTML5 RocksやGoogleの安田絹子さんの記事(Service WorkerのChrome実装者です!)を参照ください。
Service Workerは、オフラインWebを実現する最新Web標準技術の一つ。オフライン環境でも動作するWebアプリケーションの提供を可能とします。オールドファッションなオフライン Web(Application Cache)とは異なり、柔軟なキャッシュ制御が可能となるため、多様なWebアプリケーションにオフライン Webの世界を適用することができます。ネイティブアプリのようにオフラインでも動作するWebアプリ体験を提供することが可能となるわけです。
また、Service Workerは単なるオフラインアプリの実現にとどまりません。多様なキャッシュ制御ができるということは、リソース(HTMLやJavascriptファイルなど)のロード時間短縮が可能となることを意味しており、アプリキャッシュによる高速なレスポンス性を実現します。
さらに、Service Workerは、プッシュ通知もサポートします。これにより、該当のWebサイトにアクセスしていなくても、通常のモバイルアプリのようにプッシュ通知を提供することができます。Service WorkerはモバイルアプリとWebアプリとの間の様々なギャップを埋め、より高度なユーザー体験を提供するためのAPIなのです。
では、このService Workerと漸進性との関係はなんでしょう?答えは単純で、Service Workerは、それが対応していないブラウザ環境であっても、Webサイトのサービス提供自体を止めることはありません。
したがって、非対応ブラウザ利用ユーザーには、ベースとなるWebサービスのみを提供し、対応ブラウザ利用ユーザーには、オフライン対応・高速なレスポンス性・プッシュ通知といったより高度なユーザー体験を提供します。これが漸進的なWebアプリケーションと言われる所以です。
現状のWeb開発課題を解決するPRPL
さて、本題のPRPLです。これは前述のService Workerなど、様々な最新Web技術を活用したWeb開発・提供パターンです。もちろん、PWAppsの理念に基づいています。
現状のWebアプリ開発の課題。バンドルの肥大化
最近のWebアプリ開発手法は、コンポーネント化による独立性・安定性の向上と、バンドル(単一ファイルの生成)による高速性の両者に基づいています。BabelやReact、npmといったツール類を用い、ES2015やTypeScript記法による洗練されたコンポーネント志向のコーディングを行いつつ、mochaやwebpackなどを用い、オートメーション化されたテスト駆動開発と全てのコンポーネントを集約したコンパイルの実行、バンドルを行うのがベストプラクティスとなっています。
ここで、コンポーネント化のメリットは解説不要でしょう。Webアプリの各機能要素をライブラリとしてコンポーネント開発することで、独立性・安定性・再利用性といった様々な恩恵を得ることができます。
一方で、バンドル化のメリットはなんでしょうか?答えは、ロード時間の短縮です。この点は、PRPLを理解する上で重要な知識となりますので、より詳しく紹介します。
そもそも、Webはコンポーネント志向を支えるプラットフォームであると捉えることができます。それぞれのコンポーネント要素(Javascriptライブラリ、HTMLテンプレート、CSS)は、それぞれ個別のリソースファイルとして提供され、 <script> タグや、 <link> タグなどにより個別にダウンロード、ブラウザ内で一連のプログラムとして結合・コンパイルされ、Webサービスとして提供されます。
しかし、このような開発・提供手法は甚大なユーザー体験の低下を招きます。HTTP/1.1の制約により、ファイルロード時間の増大を促してしまうのです。HTTP/1.1はリクエスト&レスポンス型のデータ転送プロトコルとなっているため、リソースファイルのダウンロードが完了しないと、次のファイルのダウンロードを開始することができません。
この影響は無視することができないものであり、仮にブラウザとWebサーバー間の往復転送遅延時間を0.1秒とし、300個のコンポーネントリソースをダウンロードしなければならないとなると、0.1 x 300で合計30秒の待ち時間がサービス起動にかかってしまいます(実際には、平行ダウンロードにより数分の一に短縮されますが、極端な例として理解ください)。
これを解決するのがバンドルです。Webアプリ開発時に、サーバーサイドで一連のコンポーネントリソースを結合した一つのファイル(バンドル)を生成し、サービス提供時には、バンドルファイルのみをダウンロードすることで、転送待ち時間によるサービス起動時間を短縮する手法が用いられています(バンドル時には単に結合するだけでなく、ブラウザ依存の解決や、構文チェック・コードの最適化・重畳リソースの削減など、各種コンパイルプロセスが実行されます)。
しかし、これは新たな問題を引き起こしました。それはバンドルファイルの肥大化です。全てのコンポーネントが一つのリソースファイルに集約されるため、設計を怠るととんでもないサイズのバンドルファイルが生成されます。
最近では、SPA(Single-Page Application)の進展により、全てのルート(SPAに不慣れな方は、個別のWebページと考えていただいて結構です)を制御する一つのバンドルファイルが提供されることも珍しくありません。
このようなケースでは数百キロバイトのバンドルファイルとなることもチラホラ。ダウンロード時間の増大をもたらします。これは、モバイル環境のユーザーにとっては、とくに深刻な状況になりえます。
コンポーネント志向への回帰。PRPLによる解決
このバンドルファイルの肥大化問題を解決するのがPRPLです。最新のWeb技術を活用することで、バンドルを行わずとも、転送待ち時間によるユーザー体験の低下を解決することができます。
コンポーネントの定義
PRPLでは、各コンポーネントがWeb ComponentsのCustom Elementsで提供されることを前提とします。具体的には、各コンポーネントが カスタム要素としてHTMLファイルとして提供され、親となるHTMLからはHTML Importsにより各コンポーネントの利用が宣言されます。
この手法の重要な点は、標準化された手法によりコンポーネントの利用が宣言されること、これによりサーバーサイドでHTML Importsのリンクを解析し、Webサービスに必要となるコンポーネントツリーを得ることができます。これが、その後の HTTP/2 Server pushの適用につながります。
HTTP/2 Server pushの適用
先に解説した通り、HTML Importsで記述した場合、個別のリソースファイルの転送待ち時間によるサービス起動時間の増大が発生します。これを解決するため、PRPLではHTTP/2 Server pushが利用されます。
HTTP/2は、HTTP/1.1に続く最新バージョンのファイル転送プロトコル。HTTP/1.1のリクエスト&レスポンスによる待ち時間の制約を受けずに、複数リソースファイルを効率的・スピーディーにダウンロードすることが可能になります。
HTTP/2では、ダウンロード完了を待つことなく、以降のリソースダウンロードを行うことができます。したがって、サーバーサイドでバンドルすることなく、個別のコンポーネントファイルを高速に転送することができるのです。
PRPLでは、さらなる転送時間の短縮を図るため、HTTP/2 Server pushが用いられます。先にも書いた通り、PRPLでは各コンポーネントファイルがHTML Importsの記法により親となるHTMLファイルに記載されています。したがって、最初のHTMLファイルがダウンロード完了しないと、各コンポーネントファイルのダウンロードを開始することができません。
しかし、HTTP/2 Server pushを用いると、この待ち時間すら短縮することができます。前述の通り、PRPLでは、サーバーサイドで HTML Importsを解析し、コンポーネントツリーを得ます。そして、そのコンポーネントを最初のHTMLファイルに対するレスポンスヘッダーに記述することで、HTTP/2サーバーはクライアントからのリクエストを待つことなく、以降のコンポーネントリソースをPushすることができます。
これにより、いわゆる0 RTT(Round-Trip Time)で、全てのコンポーネントリソースをブラウザに転送することが可能になります。
Service workerによるpre cache
ここまでであげた手法により、初期起動に必要となる リソースを高速に転送し、ユーザーの起動待ち時間を短縮することが可能となりました。しかし、Webサービスは、初期画面だけではありません。その後の、ユーザー操作に基づいた各画面の提供が必要となります。
例えば、ショッピングサイトでは、各商品画面や商品購入画面が必要となります。これらを形成するコンポーネントは、初期画面に含まれるものもあるでしょうし、それ以外のものも含まれるでしょう。これらのコンポーネントリソースのダウンロード待ち時間を短縮するために、PRPLではService Workerが用いられます。
PRPLでは、初期画面用のコンポーネントリソースをService Workerを用いて、キャッシュに保存します。以降の画面で該当のコンポーネントが必要になった場合は、このキャッシュが用いられるためダウンロードは発生しません。
また、初期画面を表示したあと、以降の画面で必要となるコンポーネントリソースをバックグラウンドで事前にダウンロードし、Service Workerを用いキャッシュとして保存します(pre cache)。したがって、これらのコンポーネントリソースについても待ち時間は発生しません。非常にレスポンス性の高いWebアプリをon-the-flyで提供することが可能となります。
さらにユーザー体験を向上する lazy load
最後にlazy loadです。Webアプリのリソースには優先度があります。例えば、ファーストビューには表示されない画像ファイル、初期画面のメニューコンポーネントなどは、優先度が低いリソースの典型的なものです(メニューは、ファーストビューに含まれますが、最初からは表示されてなくてもいいですよね!)。
これらのリソースについては、lazy-loadとして、優先度の低いものとして宣言してしまいます。こうすることで、ブラウザはこれらのリソースのダウンロード順番の優先度を下げ、ファーストビューに本当に必要なものを優先して表示することができるようになります。
サンプルサイト、開発ツール
PRPLを活用したサンプルサイトとして、ショッピングサイトのサンプルが公開されています。
https://shop.polymer-project.org/
Chromeのdev tool、networkタブのwarter fallパターンから、PRPLの転送フローを確認することができます。
また、開発ツールPolymer App ToolboxではPRPLの一端が提供されています。
1 2 3 4 5 |
$ npm install -g polymer-cli $ mkdir my-app; cd my-app $ polymer init app-drawer-template #... write code ... $ polymer build |
HTTP/2対応ブラウザ、非対応ブラウザ用に、それぞれPRPLの概念に基づいた非bundle版、bundle版のjsが生成されます(こういったところに、PWAppsの概念が適用されています)。ただし、こちらの開発ツールでは、HTTP/2サーバーは提供されておらず、PRPL概念の一部を具体化したものとなっています。その点は、注意してください。
PRPLは即座に利用可能か?
さて、今回のGoogle I/Oで発表・提案された新たな開発・提供手法のPRPL。最新のWeb技術を活用することで、従来のWebアプリが内包する課題を解決し、モバイル環境での最高のユーザー体験提供を目指したものです。ただ、ここで気になるのが、以下の2点です。
- 「PRPLは今すぐ、production環境に適用できるものなのか?」
- 「本当にこれまでの開発プラクティスよりも優れているのか?」
この点について、(あくまで)個人的な見解を述べ、本稿を締めたいと思います。
まず結論から言うと、「Productionでの利用は、まだ早い」 というのが僕の率直な感想です。
その理由として、以下の点があげられます。
まだHTTP/2の環境が整備されていない
HTTP/2は、最近標準化が完了したばかり、実装はまだ完全には追いついていません。実際、apahceやnginxではHTTP/2の基本機能は搭載されているものの、Server pushまだ未サポートです。
また、これらの実装が完了したからといって、それがすぐ Productionに適用できるというものではありませんし、AmazonのELBやHerokuなどIaaS/PaaS事業者側のサポートも必要となるでしょう。WebSocketがそうであったように、サーバーサイドのエコシステムが整備されるのにはしばらく時間が必要です。
まだ開発者フレンドリーではない
開発ツールPolymer App Toolboxを見てみてもわかりますが、PRPLの概念はnpm + webpackのような開発者フレンドリーな開発手法とは相性が悪いです(実際、bowerが用いられています)。
Webアプリのコンポーネント開発において、Javascript部分が占める割合は非常に大きく、これをbowerベースで行い、いちいちHTML Importsで記述することは大変なストレスとなります。やはり、npm + webpackでストレスレスにコーディングしたい…、というのがJSエンジニアの率直な意見でしょう。
cacheとの相性が悪い
PRPLで用いられているHTTP/2 Server pushですが、Webのキャシュと非常に相性が悪いことが知られています。基本的に、ブラウザの状態と無関係にリソースをPushする仕組みのため、ブラウザにキャッシュがあろうがなかろうが、リソースをPushしてしまいます。
したがって、「初回の起動である」、「常にダイナミックに変動するリソースである」の場合を除いてHTTP/2 Server pushの効果は限定的ですし、逆に負の効果をもたらします(キャッシュがあるのであれば、リソースをPushする必要はない)。バンドルのサイズ肥大化の問題も、persistant cache制御さえちゃんとしてやれば、初回起動時およびバンドル変更時の問題だけになります。
最後に
最後にネガティブな意見を述べましたが、僕はPRPLの考えには基本的に賛同しています。Googleは常にWebをより良いものにするため、努力を続けている企業。その姿勢の表れであると感じています。
本稿で述べたPRPLの限界点は、現状のツール環境に依存するところが大きく、新たなツールが登場することでこの状況は一変することでしょう(本来、ツールは概念の一部を具現化するものであって、それをもって概念全体を評価されるべきものではありません)。
さまざまな概念やツールが融合し、開発者がフィードバックしていくことで、よりよい形が作り上げられていく。これまでのWeb開発の進化がそうであったように、そのような文脈の中でPRPLや、それのベースとなっているPWAppsが進化していくことでしょう。