シングルページアプリケーション
April 2018
シングルページアプリケーション(single-page application、SPA)とは、単一のWebページのみから構成することで、デスクトップアプリケーションのようなユーザ体験を提供するWebアプリケーションまたはWebサイトである。必要なコード(HTML、JavaScript、CSS)は最初にまとめて読み込むか、ユーザの操作などに応じて動的にサーバと通信し、必要なものだけ読み込みを行う。
ページの遷移を伴わずにサーバと通信を行う技術には様々なものがある。
Ajax: JavaScriptを用いたリクエストと非同期更新技術の総称
WebSocket: 双方向ソケット通信を可能にするプロトコル
Server-sent events: サーバからクライアントへの送信を可能にするHTTP規格
ブラウザのプラグイン: Silverlight、Flash、Javaアプレットなどのプラグインによる非同期通信
AngularJS、Ember.js、Meteor.js、Vue.js、ReactなどのJavaScriptフレームワークはSPAの原則を採用している。
AngularJSは、完全なクライアントサイドのフレームワークである。双方向データバインディングに基づくテンプレートを採用している(データバインディングとは、モデルが変化したら自動的にビューも変化させ、ビューが変化したらモデルも変化させる仕組みをいう)。HTMLテンプレートはブラウザによってコンパイルされ、生成されたHTMLがビューとしてレンダリングされる。従来のサーバサイドプログラミングではコントローラやモデルといった概念はサーバ側で作用していたが、AngularJSではそれらをクライアント側で持つため、サーバと通信することなくページを生成することが可能である。
Ember.jsは、MVCアーキテクチャに基づくクライアントサイドのフレームワークである。リッチオブジェクトモデル、宣言型の双方向データバインディング、Computed Properties、Handlebars.jsを使用した自動更新型テンプレート、状態管理可能なルーティングなど、多くのベストプラクティスを採用しており、スケーラブルなSPAの開発が可能である。
Meteor.jsは、SPAに特化して設計されたフルスタックの(クライアント・サーバ型)フレームワークである。AngularJSやEmber.js、Reactよりも単純なデータバインディングを採用している。出版-購読型モデルを採用しており、開発者が同期用のプログラムを書くことなく、データの変更をリアルタイムにクライアントに通知できる。フルスタックかつリアクティブであるため、データベースからテンプレートまでの全レイヤーが必要なときに自動的に更新される。
は、AngularJSに似たクライアントサイドのフレームワークである。AngularJSよりも新しく、より標準に準拠し、モジュール方式を取り入れている。Aureliaは次世代のECMAScriptによって書かれている。
Vue.jsは、ユーザインタフェースを構築するためのプログレッシブフレームワークである。vue-cliやwebpackによって容易にSPAを開発できる。
サーバへリクエストすると、通常は生データ(XMLやJSON)かHTMLのどちらかが送られてくる。HTMLであれば、クライアント側はJavaScriptでDOMの一部を更新する。生データであれば、クライアント側ではJavaScriptでテンプレートなどによってHTMLに変換し、その後同様にDOMの更新を行う。
クライアント側がロジックを持つ方法である。この方法ではサーバは純粋なAPIやWebサービスとなる。複雑さがサーバからクライアントに移動していることから Thin server architecture と呼ばれることがあるが、システム全体としての複雑さが削減されているかどうかは議論がある。
サーバ側でクライアントのページの状態を記憶しておく方法である。リクエストを受けるとサーバは変更を伴う具体的なHTMLやJavaScriptを返し(クライアント側はこれでDOMの一部を更新する)、同時にサーバの状態を更新する。ロジックはほとんどサーバ側で実行され、HTMLも通常サーバ側で生成される。いくつかの方法では、サーバはブラウザをシミュレートし、イベントを受け取ると、シミュレートした状態がクライアントに自動的に伝播するようにして変更を実行する。
この方法はサーバのメモリやプロセッサ資源が多く必要である。しかし、アプリケーションがすべてサーバ側で実装されること、またカスタムの通信をクライアントと持たないためサーバ内のデータや状態が同一のメモリ空間で共有できることから、開発モデルが単純化されるという利点がある。
サーバ側では状態を持たず、クライアントがリクエストの際にページの状態を送る方法である。サーバはこのデータからページを再構築し、JSONやJavaScriptといった必要なデータやコードを生成してクライアントに返す。
この方法はサーバに送るデータが大きく、リクエストのたびにサーバ側でページを再構築するため計算資源が多く必要である。一方でクライアントごとのページデータを持たないことから、セッションの共有なしにAjaxリクエストを別のサーバで処理することが可能であり、スケールしやすいという利点がある。
一部のSPAはfile URIスキームを使用してローカルファイルから実行できる。これにより、ユーザがサーバからSPAをダウンロードしたあと、サーバとの接続に依存せずにローカルストレージからファイルを実行することが可能である。このようなSPAでデータを保存・更新したい場合は、ブラウザベースのWeb Storageを使用する必要がある。
SPAはブラウザがもともと考慮していたステートレスなページ遷移モデルとは大きく異なるため、いくつか新たな課題が生じている。しかし、以下のものでそれらに対処することが可能である。
様々な問題に対処したクライアントサイドのJavaScriptライブラリ。
SPAに特化したサーバサイドのフレームワーク。
ブラウザの進歩、およびSPAを考慮して設計されたHTML5の仕様の進歩。
いくつかの一般的な検索エンジンのクローラではJavaScriptが実行されないため、SPAを採用しようとしているWebサイトにとってSEOはかねての課題である。これはSPAにとって些細な問題ではなく、検索エンジンへのインデックス付けが必須または望ましい状況では一般的にSPAは使用されない。
2009年以降、Googleは「AJAX crawling scheme」を提案・推奨していた。これはクローラが必要なメタデータを抽出できるよう、SPAに特別な動作の実装を要求するものであった。この提案は2015年に非推奨となった。
Webサイトがクロール可能であるかのようにする方法はいくつかある。サーバでHTMLベースのサイトを生成してそれをクローラに渡す方法や、PhantomJSなどのヘッドレスブラウザでJavaScriptアプリケーションを実行してHTMLを出力する方法である。これらはいずれもSPAとは別個にHTMLページを作成しなければならない。かなりの労力が必要であるし、大規模なサイトではメンテナンスに苦労することになる。さらには、SEOの落とし穴も存在する。サーバが生成したHTMLがSPAの内容と大きく異なるとみなされると、サイトにペナルティが課せられる。PhantomJSでHTMLを出力した場合は、ページのレスポンスが遅くなる可能性があり、これは検索エンジン、特にGoogleにおいてランクを低下させる要因となる。
単一のページしか持たないSPAは、ブラウザの「戻る」「進む」ボタンによるページの履歴ナビゲーションと相性が悪い。ユーザがSPA内の前の画面を期待して戻るボタンを押したにもかかわらず、SPAがアンロードされ、ブラウザの履歴における前のページを表示することになり、ユーザビリティの障害となる。
従来のSPAにおける解決策は、ブラウザのURLのフラグメント識別子(ハッシュ)を画面状態に応じて変更することであった。これはJavaScriptによって実現でき、URL履歴イベントをブラウザ内に構築する。SPAがURLハッシュに含まれる情報から同じ画面状態を復元できる限り、期待される戻るボタンの動作は保持される。
この問題へのさらなる対処として、HTML5ではブラウザの履歴操作の機能を提供する HTML5 History API が導入された 。
Google Analyticsなどの解析ツールは、最初のロードによって開始されたあと、ブラウザによるページの読み込みに依存して動作する。しかし、SPAではこの方法をとることができないため、リソースの更新時にトラッカーを更新する関数を明示的に呼び出すことによって対処する。呼び出しに失敗すると、解析ツールはサイト上で誰が何をしているのかわからない。
HTML5 History APIを使うとSPAにページの読み込みイベントを追加でき、解析に有用である。また、フレームワークによっては主要なアナリティクスサービスに対応するオープンソースの統合解析ツールを提供している。
SPAはサーバベースのアプリケーションよりも最初のページの読み込みが遅くなる。これはビューをレンダリングする前にフレームワークとアプリケーションコードを読み込まなければならないためである。サーバベースのアプリケーションであれば必要なHTMLを取得するだけでよいため、通信にかかる時間は短い。
キャッシングや遅延ロードモジュールなど、SPAの最初の読み込みを高速化する方法はいくつかある。しかし依然として、フレームワークとアプリケーションコードの少なくとも一部をダウンロードし、かつ多くの場合においてブラウザ上に何らかの表示をするためにAPIによるデータ取得が必要である、という事実は変わらない。このパフォーマンスと待ち時間の問題は、「いま払うか、あとで払うか」というトレードオフであり、開発者が決定しなければならないものである。