PWA の信頼性、インストール可能性、機能性を高めるテクノロジーを最大限に活用できるようにアプリケーションを設計するには、まず、アプリケーションとその制約を理解し、両者に適したアーキテクチャを選択します。
SPA と MPA の比較
現在、ウェブ開発には、シングルページ アプリ(SPA)とマルチページ アプリ(MPA)の 2 つの主要なアーキテクチャ パターンがあります。
シングルページ アプリとは、アプリが取得または提供するデータに基づいて、ページの HTML レンダリングのほとんどまたはすべてをクライアント側の JavaScript に制御させることで定義されます。アプリはブラウザの組み込みナビゲーションをオーバーライドして、ルーティングとビューの処理機能に置き換えます。
マルチページ アプリは通常、プリレンダリングされた HTML をブラウザに直接送信します。多くの場合、ブラウザが HTML の読み込みを完了した後にクライアント側の JavaScript で拡張し、ブラウザの組み込みのナビゲーション メカニズムを使用して後続のビューを表示します。
どちらのアーキテクチャでも PWA を作成できます。
それぞれに長所と短所があります。ユースケースとコンテキストに適したものを選択することは、ユーザーに高速で信頼性の高いエクスペリエンスを提供するうえで重要です。
シングルページ アプリ
- ほとんどの場合はアトミックなページ内更新です。
- 起動時に読み込まれるクライアント側の依存関係。
- キャッシュが使用されるため、後続の読み込みは高速になります。
- 初期読み込みのコストが高い。
- パフォーマンスはデバイスのハードウェアとネットワーク接続によって異なります。
- アプリの複雑さが増す必要がある。
シングルページ アプリは、次のようなアーキテクチャに適しています。
- ユーザーの操作は主に、リアルタイム データ ダッシュボードや動画編集アプリなど、同じページに表示される相互接続されたデータのアトミックな更新を中心に行われます。
- アプリケーションに、クライアントサイドのみの初期化依存関係がある(起動費用が非常に高いサードパーティの認証プロバイダなど)。
- ビューの読み込みに必要なデータは、特定のクライアント側のみのコンテキストに依存します。たとえば、接続されているハードウェアのコントロールを表示します。
- アプリが小さくシンプルなため、サイズと複雑さが上記の短所に影響を与えない。
次の場合は、SPA が適切なアーキテクチャを選択しない可能性があります。
- 初期読み込みのパフォーマンスは重要です。SPA では通常、何を読み込むか、どのように表示するかを決定するために、より多くの JavaScript を読み込む必要があります。この JavaScript の解析と実行にかかる時間は、コンテンツの取得にかかるため、レンダリングされた HTML を送信するよりも遅くなります。
- お客様のアプリは、ほとんどが平均性能の低いデバイスで実行されます。SPA ではレンダリングに JavaScript が使用されるため、ユーザー エクスペリエンスは MPA よりも特定のデバイスの性能に大きく依存します。
SPA ではブラウザの組み込みナビゲーションをルーティングに置き換える必要があるため、現在のビューの更新、ナビゲーションの変更の管理、以前のビューのクリーンアップなど、ブラウザが処理すべきものについて、最低限の複雑さが要求されます。これにより、全体的なメンテナンスが難しくなり、ユーザーのデバイスへの負担が増えます。
マルチページ アプリ
- ほとんどの場合、ページ全体を更新します。
- 最初のレンダリング速度は非常に重要です。
- クライアントサイド スクリプトを拡張機能として利用できます。
- セカンダリ ビューには別のサーバー呼び出しが必要です。
- コンテキストはビュー間で引き継がれません。
- サーバーまたはプリレンダリングが必要です。
マルチページ アプリは、次のようなアーキテクチャの場合に適しています。
- ユーザー インタラクションは主に、単一のデータと任意のコンテキスト ベースのデータ(ニュースアプリや e コマースアプリなど)の表示を中心としています。
- 初期のレンダリング速度は非常に重要です。すでにレンダリングされた HTML をブラウザに送信する方が、JavaScript ベースの代替の読み込み、解析、実行を行った後に、データ リクエストから組み立てるよりも速いためです。
- クライアントサイドのインタラクティビティまたはコンテキストは、初期読み込み後の機能強化として含めることができます。たとえば、レンダリングされたページにプロファイルをレイヤ化したり、クライアントサイドのコンテキスト依存のセカンダリ コンポーネントを追加したりします。
次の場合は、MPA は適切なアーキテクチャを選択しない可能性があります。
- JavaScript または CSS の再ダウンロード、再解析、再実行には多大なコストがかかります。このデメリットは、Service Worker を使用する PWA では緩和されます。
- クライアントサイドのコンテキスト(ユーザーの位置情報など)はビュー間でシームレスに引き継がれず、そのコンテキストの再取得にはコストがかかる可能性があります。キャプチャして取得するか、ビュー間で再リクエストする必要があります。
個々のビューは、アクセス前にサーバーによって動的にレンダリングされるか、プリレンダリングされる必要があるため、ホスティングが制限されたり、データが複雑になったりする可能性があります。
どれを選びますか?
このような長所と短所があっても、どちらのアーキテクチャも PWA の作成には有効です。ニーズに応じて、アプリのさまざまな部分に合わせてこれらを組み合わせることもできます。たとえば、ストアの掲載情報は MPA アーキテクチャに従い、購入手続きは SPA アーキテクチャに準拠させるなどです。
どのような選択肢であっても、次のステップは、最適なエクスペリエンスを提供するための Service Worker の最適な使用方法を理解することです。
Service Worker の機能
Service Worker は、基本的なルーティングや、キャッシュされたネットワーク レスポンスの配信にとどまらない、強力な機能を備えています。ユーザー エクスペリエンスとパフォーマンスを向上させる複雑なアルゴリズムを作成できます。
Service Worker に含まれるもの(SWI)
Service Worker をサイトのアーキテクチャに不可欠な要素として使用する新たなパターンとして、Service Worker インクルード(SWI)があります。SWI は、キャッシュのニーズに基づいて個々のアセット(通常は HTML ページ)を分割し、Service Worker でそれらをつなぎ合わせて、キャッシュ サイズを削減しながら、一貫性、パフォーマンス、信頼性を向上させます。
この画像はサンプルのウェブページです。このページは 5 つのセクションに分かれており、各セクションは次のように分けられています。
- 全体のレイアウト。
- グローバル ヘッダー(上部の濃いバー)。
- コンテンツ エリア(左中央の線と画像)。
- サイドバー(右側にある背の高いやや濃いバー)。
- フッター(下部の濃いバー)。
全体のレイアウト
全体的なレイアウトは頻繁に変更される可能性は低く、依存関係もありません。プレキャッシュに適しています。
ヘッダーとフッター
グローバル ヘッダーとフッターには、トップメニューやサイトのフッターなどが含まれ、特定の課題があります。ページ全体をキャッシュする場合、ページがいつキャッシュされたかに応じて、ページの読み込みと読み込みの間でこれらが変更される可能性があることです。
これらを分離して、コンテンツとは別にキャッシュに保存することで、ユーザーがいつキャッシュされたかにかかわらず、常に同じバージョンを取得できます。更新頻度が低いため、事前キャッシュにも適しています。ただし、サイトの CSS と JavaScript という依存関係があります。
CSS と JavaScript
理想的には、サイトの CSS と JavaScript を古い再検証戦略でキャッシュして、Service Worker を更新せずに増分更新できるようにします。これは、事前キャッシュされたアセットの場合と同様です。それでも、Service Worker が新しいグローバル ヘッダーまたはフッターで更新されるたびに、最小バージョンを維持する必要があります。このため、Service Worker のインストール時にキャッシュも最新バージョンのアセットで更新する必要があります。
コンテンツ領域
次はコンテンツ領域ですここでは、更新の頻度に応じて、ネットワーク優先にするか、再検証中に最新でない状態を維持するかをおすすめします。以前に説明したように、画像はキャッシュ ファースト戦略でキャッシュに保存する必要があります。
サイドバー
最後に、サイドバーのコンテンツにタグや関連アイテムなどの二次的なコンテンツが含まれていると仮定した場合、ネットワークから取得するほどの重要度はありません。これには古い再検証戦略が有効です。
ここまでの説明を踏まえて、このようなセクションごとのキャッシュ保存はシングルページ アプリではできないと思えるかもしれません。しかし、エッジサイド インクルードやサーバーサイド インクルードにヒントを得たパターンを Service Worker に採用し、高度な Service Worker 機能を使用することで、どちらのアーキテクチャでもこれを実現できます。
実際に試す
次の Codelab では、Service Worker のインクルードを試すことができます。
ストリーミング レスポンス
前のページは、SPA の世界では App Shell モデルを使用して作成できました。ここでは、App Shell がキャッシュされ、配信され、コンテンツがクライアント側で読み込まれます。Streams API が導入され、広く利用できるようになったことで、App Shell とコンテンツの両方を Service Worker で組み合わせ、ブラウザにストリーミングできるようになりました。これにより、App Shell の柔軟なキャッシュと MPA のスピードが実現します。
その理由は次のとおりです。
- ストリームは非同期的に構築できるため、ストリームのさまざまな部分を他のソースから取り込むことができます。
- ストリームのリクエスト元は、アイテム全体が完了するまで待つのではなく、最初のデータチャンクが利用可能になり次第、レスポンスの処理を開始できます。
- ブラウザを含め、ストリーミング用に最適化されたパーサーは、ストリームが完了する前にコンテンツを徐々に表示できるため、認識されるレスポンスのパフォーマンスが速くなります。
ストリームの 3 つの特性のおかげで、ストリーミングを中心に構築されたアーキテクチャは、そうでないアーキテクチャよりも、認識されるパフォーマンスが通常速いのです。
Streams API は複雑でローレベルであるため、操作は難しい場合があります。幸いなことに、Service Worker 向けにストリーミング レスポンスを設定するのに役立つ Workbox モジュールがあります。
ドメイン、オリジン、PWA スコープ
ウェブ ワーカー(Service Worker、ストレージ、インストールされている PWA のウィンドウを含む)はすべて、ウェブ上で最も重要なセキュリティ メカニズムの一つである同一オリジン ポリシーによって管理されます。同じオリジン内で権限が付与され、データを共有できます。また、Service Worker が異なるクライアントと通信できます。オリジンが同じ外では、権限は自動的に付与されず、データは分離され、異なるオリジン間でアクセスできません。
同一オリジン ポリシー
プロトコル、ポート、ホストが同じである 2 つの URL は、発信元と完全に一致するものとして定義されます。
たとえば、https://squoosh.app
と https://squoosh.app/v2
のオリジンは同じですが、http://squoosh.app
、https://squoosh.com
、https://app.squoosh.app
、https://squoosh.app:8080
のオリジンは異なります。詳細と例については、同一オリジン ポリシー MDN リファレンスをご覧ください。
ホストを変更できる方法は、サブドメインの変更だけではありません。各ホストは、トップレベル ドメイン(TLD)、セカンダリ レベル ドメイン(SLD)、0 個以上のラベル(サブドメインとも呼ばれます)で構成されます。これらのラベルはドットで区切られ、URL は右から左に記述します。いずれかの項目を変更すると、別のホストになります。
ウィンドウ管理モジュールでは、インストール済みの PWA とは異なるオリジンにユーザーが移動した場合のアプリ内ブラウザの表示をすでに確認しました。
このアプリ内ブラウザは、ウェブサイトが同じ TLD と SLD を持つ場合でも、異なるオリジンと見なされるため、異なるラベルで表示されます。
ウェブ ブラウジングのコンテキストにおける送信元の重要な側面の一つは、ストレージと権限の仕組みです。1 つのオリジンは、その中のすべてのコンテンツと PWA で以下のような多くの機能を共有します。
- 保存容量とデータ(IndexedDB、Cookie、ウェブ ストレージ、キャッシュ ストレージ)
- Service Worker の登録。
- 許可または拒否されている権限(ウェブプッシュ、位置情報、センサーなど)。
- ウェブプッシュ登録。
オリジンを別のオリジンに移動すると、それまでのアクセスはすべて取り消されるため、権限を再度付与する必要があります。PWA はストレージに保存されているすべてのデータにアクセスできなくなります。