任意のサーバー技術を利用してApp Toolboxのアプリを配信することができます。Polymer CLIのビルドプロセスは、以下の二つのビルドを生成することで、最新のWebテクノロジーを駆使た高速読み込みが可能なアプリケーションを提供することができます。:
-
unbundledビルド:リソースの配信にHTTP/2とHTTP/2のサーバープッシュを組み合わせを使っているサーバー/ブラウザ向けに設計されたものです。この場合、ブラウザは、キャッシングを最適化しながら初回の描画を高速化することを要求します。
-
bundledビルド:サーバー/ブラウザ間でサーバープッシュをサポートしていない実行環境において、アプリケーションの取得に必要なラウンドトリップの回数を最小限に留めるように設計されています。
サーバーロジックは、ブラウザごとに適切なビルドを供給することができます。
PRPLパターン
配信を最適化するために、ToolboxはPRPLパターンを使用します。:
- 重要なリソースを初期ルート(initial route)へプッシュ(Push)します。
- 初期ルートをレンダリング(Render)します。
- 残りのルートをプレキャッシュ(Pre-cache)する。
- 必要に応じて残りのルートをレイジーロード(Lazy-load)し生成します。
これらを実行するために、サーバーはアプリケーションの各ルートで必要とされるリソースを識別できるようにする必要があります。リソースを一つのダウンロード単位にバンドルする代わりに、HTTP2プッシュを使用して、要求されたルートのレンダリングに必要なリソースを個別に配信します。
サーバーとサービスワーカーは、非アクティブなルートに対するリソースを事前にキャッシュするために連動して動作します。
ユーザーがルートを切り替えると、アプリケーションは未キャッシュだが必要されたリソースをレイジーロード(lazy-load)し、要求されたビューが生成されます。
アプリケーションの構造
現在、Polymer CLIおよびリファレンスサーバーは、以下のような構造のシングルページアプリケーション(SPA)をサポートしています。(訳注:下の図を参照しながら説明を読んで下さい。):
- すべての有効なルートから供給されるアプリケーションのメインのエントリーポイント(index.html)。このファイルは可能な限り小さくすべきです。異なるURLから提供されるため、何度もキャッシュされるかもしれないからです。エントリポイント内のすべてのリソースのURLは、トップレベルではないURLからも提供される可能性があるので、絶対パスを使用する必要があります。
- シェルやapp-shellには、トップレベルのアプリケーションロジックやルーターなどが含まれます。
- レイジーロードされるアプリケーションのフラグメント。フラグメントとして、個々のビューのコードやレイジーロード可能なその他コード(例えば、ユーザーがアプリケーションとやりとりするまで表示されないメニューのように、最初の描画に不要なメインアプリケーションの一部)を表すことができます。シェルには、必要に応じてフラグメントを動的にインポートする責任があります。
下の図は、単純なアプリケーションのコンポーネントを示しています。:
この図では、実線は静的な依存関係を表し、<link>
タグと<script>
タグを使ってファイル内で識別される外部リソースを表しています。点線は、動的な依存関係や読み込み要求のあった依存関係(シェルからの要求に応じて読み込まれるファイル)を表現しています。
ビルドプロセスでは、これらのすべての依存関係に関するグラフを構築し、サーバーはこの情報を利用してファイルを効率的に提供します。また、HTTP2のプッシュをサポートしていないブラウザ向けに、vulcanizeされた各種バンドルも生成します。
アプリケーションのエントリーポイント
エントリーポイントは、シェルをインポートしてインスタンス化するだけでなく、必要な場合にはポリフィルをロードします。
エントリーポイントの主な考慮事項は次のとおりです。;
- 静的な依存は最小限に抑えます。これはapp-shellそのものの依存より遥かに少ないものです。
- 必要であれば条件に応じてポリフィルをロードします。
- すべての依存関係に対して絶対パスを使用します。
Polymer CLIを使用してApp Toolboxプロジェクトを生成すると、新しいプロジェクトにはエントリーポイントindex.html
が含まれます。ほとんどのプロジェクトでは、このファイルを書き換える必要はないはずです。
アプリケーションのシェル(App Shell)
シェルはルーティングを担当し、通常はアプリケーションのメインナビゲーションUIが含まれます。
アプリケーションは、フラグメントが必要とされるまで読み込まれるのを遅延するために、importHref
を呼び出す必要があります。例えば、ユーザーが新しいルートに変更すると、そのルートに関連付けられたフラグメントがインポートされます。これにより、サーバーへの新たなリクエストが送られるか、あるいは、キャッシュからリソースがロードされるだけかもしれません。
importHrefの例(クラススタイル版)
// get a URL relative to this element
let resolvedUrl = this.resolveUrl('list-view.html');
// import the file
Polymer.importHref(
resolvedUrl,
null, /* callback for successful load -- usually not needed */
this._importFailedCallback.bind(this), /* for example, display 404 page */
true); /* make import async */
importHrefの例(ハイブリッド版)
var resolvedPageUrl = this.resolveUrl('my-' + page + '.html');
this.importHref(resolvedPageUrl,
null,
this._importFailedCallback,
true);
シェル(静的な依存関係を含む)には、最初の描画に必要なものが全て含まれている必要があります。
ビルドの出力
Polymer CLIのビルドプロセスは、次の二つのビルドを生成します。:
-
unbundledビルド:リソースの配信にHTTP/2とHTTP/2のサーバープッシュを組み合わせを使っているサーバー/ブラウザ向けに設計されたものです。この場合、ブラウザは、キャッシングを最適化しながら初回の描画を高速化することを要求します。
-
bundledビルド:サーバー/ブラウザ間でサーバープッシュをサポートしていない実行環境において、アプリケーションの取得に必要なラウンドトリップの回数を最小限に留めるように設計されています。
polymer build
コマンドは、並行して二つのビルドを出力先のフォルダに生成します。:
build/
unbundled/
index.html
...
bundled/
index.html
...
サーバーロジックは、ブラウザごとに適切なビルドを供給する必要があります。
Bundledビルド
HTTP2のプッシュを扱わないブラウザの場合、ビルドプロセスは、vulcanizeされたバンドルを生成します。一方はシェル用のバンドルで、もう一方は各フラグメント用のバンドルです。下の図は、単純なアプリケーションがどのようにバンドルされるかを示しています。:
二つ以上のフラグメントによって共有された依存関係はすべて、シェルとその静的な依存関係にバンドルされています。
各フラグメントと共有されていない静的な依存関係は、一つにバンドルされています。サーバーは、ブラウザに応じて、適切なバージョンのフラグメント(bundledまたはunbundled)を返す必要があります。つまり、シェルのコードは、bundledかunbundledか知らなくても、detail-view.html
をレイジーロードできるということです。依存関係が最も効率な方法でロードされるかは、サーバーとブラウザ次第です。
HTTP/2とHTTP/2サーバープッシュの背景
HTTP/2を使用すると、単一の接続上で多重ダウンロードを行うことができるため、複数の小さなファイルをより効率的にダウンロードできます。
HTTP/2のサーバープッシュにより、サーバーはブラウザからの要求を先取りしてリソースを送信できます。
HTTP/2のサーバープッシュのダウンロード速度をどのように上げるかについては、ブラウザがスタイルシートのリンクを使ってどのようにHTMLファイルを読み出しているか考察してみてくだい。
HTTP/1では:
- ブラウザがHTMLファイルを要求します。
- サーバがHTMLファイルを返し、ブラウザが解析を開始します。
- ブラウザは
<link rel="stylesheet">
タグを見つけると、スタイルシートの新しい要求を開始します。 - ブラウザはスタイルシートを受信します。
HTTP/2のプッシュを利用すると:
- ブラウザがHTMLファイルを要求します。
- サーバーはHTMLファイルを返すと同時にスタイルシートをプッシュします。
- ブラウザがHTMLの解析を開始します。
<link rel="stylesheet">
が見つかりますが、スタイルシートはすでにキャッシュに入っています。
この最も単純なケースでは、HTTP/2のサーバープッシュはHTTPのリクエスト/レスポンスを一つなくすことができました。
HTTP/1では、開発者はリソースをまとめてバンドルすることで、ページのレンダリングに必要なHTTPリクエストの数を減らします。ただし、バンドルすることでブラウザのキャッシュ効率が低下する可能性があります。仮に各ページのリソースが一つのバンドルに結合されているとしたら、個々のページはそれぞれ独自のバンドルを取得することになり、ブラウザは共有リソースを識別することができません。
HTTP/2とHTTP/2のサーバープッシュの組み合わせは、リソースをバンドルすることなく、バンドル処理の利点(遅延時間の短縮)を享受できます。リソースを分割しておくことで、これらは効率的にキャッシュされページ間で共有することができます。