今回は、前回に引き続きMeteorのリアクティブ・プログラミングについて説明します。
Meteorアプリケーションでは、あるクライアント上で行ったデータ変更が、他のクライアントにもリアルタイムに伝わります。しかも、そのために必要なコードはごくわずかで、ほぼ自動的に処理されるといっても過言ではありません。
その仕組みは、「クライアント上のリアクティビティ」と、「publish/subscribeによる、サーバデータとクライアントキャッシュの同期」によって実現されています。
前回、クライアント上でのリアクティビティについてはお伝えしました。今回はpublish/subscribeについて解説し、リアクティビティについても復習します。
publish/subscribeによるデータ同期
Meteorでは、サーバ上のデータはクライアントにキャッシュされ、リアルタイムに同期されます。同期は自動的に、しかも双方向に行われるため、開発者が明示的に同期処理を記述する必要はありません。必要とされるのは、Meteor.publish()
とMeteor.subscribe()
を使用し、データを同期する範囲を指定することだけです。
publish
とsubscribe
の説明のため、まずは前提条件として、「従業員データ」を格納する以下のようなコレクションがあると仮定します。このコレクションは、クライアントとサーバの双方でインスタンス化されます。
サーバ側で実行されるMeteor.publish()
は、名前を表す文字列と、関数オブジェクトを引数に取ります。
引数として渡す関数オブジェクトは、データのコレクションを戻り値として返します。この戻り値となったコレクションが、クライアント側のキャッシュと自動的に同期されます。上の例では、60歳以上の従業員データが返されます。
対してMeteor.subscribe()
はクライアント側で呼び出され、publish
によって公開されたデータを「購読」(同期して取り込むこと)します。
ここに挙げた例では、サーバデータのうち「60歳以上」のデータのみが、クライアントのコレクションに同期されます。
publish
とsubscribe
は、Meteorプログラミングにおいては非常に重要なAPIです。以降では、これらのAPIについてさらに詳しく説明します。
Meteor.publish()
Meteor.publish()
の定義は以下のとおりです。
- name…名前を表す文字列。クライアントがサブスクライブを行う際に、この名前が使用される。
- func…この関数は、クライアントからのサブスクライブが行われるたびに呼び出されます。
第二引数func
の目的は、クライアント側のキャッシュと同期するデータ(コレクション)を指定にすることです。そのひとつの方法としては、コレクションのカーソル(Meteor.Collection.Cursor
)を戻り値とすることです。上記の例を再掲します。
もうひとつの方法としては、publish
内でコレクションに変更を行い、その旨をクライアントに通知することです。こうした操作は、publish
内のthis
オブジェクトが持つ以下のようなメソッドを通じて行います。
added(collectionName, id, fields)
- …コレクション名、ドキュメントのID(_idフィールド)、追加されたドキュメント(JavaScriptオブジェクト)を引数に取り、ドキュメントが追加されたことをクライアントに通知します。
changed(collectionName, id, fields)
- …コレクション名、ドキュメントのID(_idフィールド)、変更されたドキュメント(JavaScriptオブジェクト)を引数に取り、ドキュメントが変更されたことをクライアントに通知します。
removed(collectionName, id)
- …コレクション名、ドキュメントのID(_idフィールド)を引数に取り、ドキュメントが削除されたことをクライアントに通知します。
また、publish
内のthis
オブジェクトは、以下のようなメソッドも保持しています。
userId
- …ログインユーザのID。ログインされていなければnull。
ready()
- …クライアントに対し、初期データの送信が終わったことを伝える。
Meteor.subscribe()
のコールバックが呼び出される。
onStop(func)
- …パブリッシュが停止した際に呼び出されるイベントハンドラを登録する。
error(error)
- …クライアントにエラーを通知する。クライアントからのサブスクライブは停止し、
Meteor.subscribe()
のコールバックが呼び出される。
stop()
- …パブリッシュを停止する。
また、func
は引数を取ることもできます。func
に引数として渡される値は、クライアントがMeteor.subscribe()
で渡したものになります。よくある例としては、クライアントが検索条件などを指定してサブスクライブを行う場合に、検索条件を渡す用途で使用されます。
Meteor.subscribe()
Meteor.subscribe()
は、パブリッシュされたデータをクライアント側から「サブスクライブ(購読)」するために、クライアント側で呼び出されます。Meteor.subscribe()
の定義は以下のようになっています。
- name
- …サブスクリプションの名称(
publish
で指定した名前)
- args
- …
Meteor.publish()
に渡す引数。任意の数の引数を渡すことができます。
- callbacks
- …関数、もしくはJavaScriptオブジェクトを指定出来ます。JavaScriptオブジェクトを指定する場合、
onReady
(初期化完了時に呼び出される)やonError
(エラー時に呼び出される)というコールバックプロパティを持つことができます。関数を指定した場合は、onReady
を指定したのと同様になります。
また、戻り値は以下のメンバーを持つJavaScriptオブジェクトです。
- stop()
- …サブスクリプションを停止することができます。
- ready()
- …このサブスクリプションが「準備完了」状態になっているかどうか(
publish
の節におけるready()
メソッドの説明を御覧ください)を返します。リアクティブ・データソースであり、この値が変化すると、リアクティブ・コンテキストの再評価が行われます(リアクティブ・データソースやリアクティブ・コンテキストについては前回の記事を御覧ください)
Meteor.autorun()を用いてsubscribeを自動的に再実行する
Meteor.autorun()
は、前回の記事でも紹介しました。autorun()
は任意の関数を受け取ります。その関数は即座に実行され、同時に関数内部で使用されているリアクティブ・データソースが記憶されます。そのデータソースに変化があると、autorun()
に渡された関数は自動的に再実行されます。
Meteor.autorun()
内でMeteor.subscribe()
を使用していると、再実行時にサブスクリプションの再構築が行われます。例えば、検索条件を指定したサブスクライブを行なっている場合などに、検索条件が変化したらサブスクリプションの再構築を行う、と言った用途に使用出来ます。
サンプル1: 各種APIの利用例
以上で紹介したさまざまなAPIを利用している簡潔なコード例として、まずはMeteorのドキュメントから引用します。この例のポイントは、Meteor.publish()
の内部でthis.added
、this.ready()
などのAPIを利用している点です。この例はチャットアプリケーションを想定しており、チャットルームごとの人数を保持するために「counts」というコレクションを定義しています。
サンプル2: 実際に動作するサンプル
また、実際に動作させることのできる単純なサンプルとして、連載第9回で使用した従業員データベースに機能を追加したものをご紹介します。
前回のサンプルとの違いは、従業員の年代別に絞り込みを行えるようにしたことです。一覧表の上部に、年代ごとに絞り込みを行える選択リストを置きました。このリストでは、「絞り込みを行わない」「10代」「20代」...「60代以上」を選択することができます。
サンプルコードは以下のリンクからダウンロードすることができます。
以下に、今回のサンプルのポイントを示します。
まず、今回追加した選択リストは、以下のようなHTMLとなっています。option要素の値が、そのまま検索条件のパラメータとして利用されます。
そして、このリストの選択状態が変化したら、以下のイベントハンドラが呼び出されます。セッションに、選択された値をセットします。
クライアント側のコードで、最大のポイントがここです。Meteor.autorun()
を使用してサブスクライブを行っていますが、autorun()
に渡した関数の内部でセッションの値にアクセスしています。したがって、「リストの選択状態が変化する→セッションに選択値がセットされる→サブスクライブし直す」という流れが実現できていることになります。
そして、サーバ側のコードを見てみます。クライアントがサブスクライブしている'emp'というコレクションは、以下のようにパブリッシュされています。
たったこれだけのコード追加で、検索条件に応じてデータの絞り込みを行い、かつ、それらをリアルタイムに監視するアプリケーションができてしまいます。実際にサンプルコードを動作させて、いかにMeteorのリアクティブ・プログラミングが強力かを体感してみてください(ブラウザウィンドウを2枚開いて動作させて見ることをオススメします)。
まとめ
前回と今回で、Meteorのリアクティブ・プログラミングについて解説しました。リアクティビティはMeteorにとって最大のウリのひとつです。ぜひそのパワーを理解し、新時代のWebアプリ体験に触れてみてください。