リアクティブ・プログラミング
今回お伝えするのは、Meteorが一般的なWebフレームワークとは一線を画しているコンセプト、「リアクティブ・プログラミング」についてです。
リアクティブ・プログラミングとは、端的に言うと、あるデータに対して行った変更が自動的に伝播し、その他の部分に影響をおよぼすようなプログラミング・パラダイムのことです。
たとえば表計算ソフトの一般的な機能として、セルの値に応じて、そのセルを参照している部分の値も自動的に変更されるというものがありますが、これはリアクティブ・プログラミングの典型的な例です。
また、MVCフレームワークの中には、モデルの値を変更すると自動的にビューが更新されるという機能を持つ(バインディングと呼ばれます)ものもありますが、これもリアクティブ・プログラミングの例と言って良いでしょう。
Meteorの素晴らしい点は、リアクティブ・プログラミングをフレームワーク全体でサポートしていることです。Meteorは御存知の通り、クライアントからサーバまでカバーする「フルスタック・フレームワーク」ですから、その恩恵はWebアプリケーションスタック全体に及びます。
たとえば第8回のサンプル(8-1)を2つのウィンドウで開き、片方のウィンドウ上でデータを変更します。すると、もう片方のウィンドウの値も自動的に変更されます ─そんな、データを同期するようなコードはどこにも書いていないのに!!
この現象は、以下のような流れで起こっています。
- 片方のクライアントが、キャッシュされたデータを変更する
- サーバのデータにも自動的に変更が伝わる
- そのサーバデータを利用している他のクライアントにも、自動的に変更が伝わり、キャッシュが更新される
- キャッシュが更新されると、自動的にビューが更新される
2~4までの処理がすべて「自動的に」行われていることがおわかりでしょう。このように、サーバとクライアント間のネットワークすらもまたいで、データ変更が自動的に伝播するというのが、Meteorによるリアクティブ・プログラミングのパワーです。
上記のようなMeteorの動作は、具体的には以下のような要素によって成り立っています。
- クライアント上のリアクティビティ
- publish/subscribeによるサーバデータとクライアントキャッシュの同期
それぞれを理解するのは決して難しくありません。今回は、クライアント上のリアクティビティについて説明します。
クライアントサイドでのリアクティビティ
Meteorが提供するクライアントサイドでのリアクティビティにより、たとえば以下のようなことが実現されます。
- データに変更があった場合、ビューが自動的に変更される
- 設定値を保持する変数に変更があった場合、自動的にサーバデータとの同期が再実行される
- ...
ここで挙げた例はどれも、「○○(というデータ)に変更があった場合」「××(という関数)が実行される」という形になっているのがおわかりでしょう。前者(○○というデータ)のことをリアクティブ・データソース、後者(××という関数)のことをリアクティブ・コンテキストと呼びます。
Meteorがデフォルトで用意しているリアクティブ・データソースには以下のようなものがあります。
- Session…メモリ上に展開される、汎用的なキー・バリューストア。
Session
というグローバル変数で表され、任意のデータを自由に格納できる。
- データベースクエリの結果…コレクションの
find
を実行した結果。
- Meteor.status()…クライアントとサーバの間の接続状況。
- Meteor.user()…現在ログイン中のユーザ
- Meteor.userId()…現在ログイン中のユーザID
- Meteor.loggingIn()…現在ログイン中かどうか
これらのデータソースに変更があった場合、以下に示すリアクティブ・コンテキストに渡した関数オブジェクトが、自動的に再実行されます。
- HTMLテンプレート…連載4~6回を参照。Meteorのビューを構築する中心的な技術。
- Meteor.render()/Meteor.renderList()…HTMLテンプレート内で使用される。
- Meteor.autosubscribe()…メソッド内で実行された
Meteor.subscribe
については、再実行時に自動的に停止・再開してくれる。
- Meteor.autorun()…任意の関数を受け取り、データソースの変更に際して自動的に再実行できる。
リアクティブ・プログラミングを体感するためのサンプル
では、サンプルを通じてリアクティブ・プログラミングを体感していきましょう。
前述したリアクティブ・データソースの中でも、Session
オブジェクトは使用頻度が非常に高いものです。その実態は、キー・バリューという形式でデータを保持できる、いわゆる「ハッシュテーブル」でしかないのですが、セッションに加えた変更が自動的に伝播するのが強みです。
Session
オブジェクトは以下のようなメソッドを持っています。
- set(key, value)…セッションに値をセットする
- get(key)…セッションから値を取得する
- equals(key, value)…指定したキーの値が、valueと同一であるかどうかを返す。「===」演算子での比較と結果は同じだが、リアクティブなのが嬉しい。
Session
を使用した、簡単なサンプルがこちらです。入力フィールドに格納された値を、その下に表示されているエリアにエコーします。
このサンプルは、以下のリンクからダウンロードできます。
まず、HTMLのソースコードです。
入力フィールドが'msg'というIDを持つこと、出力箇所が'result'という変数で表されていることを確認してください。
次にJavaScriptのソースコードです。
図中で番号を振ってある部分がポイントです。以下に簡単な説明を加えます。
(1) テキストフィールドに入力が発生したら、その値をSession.set()
に格納しています。ここで、リアクティブ・コンテキストに対して変更が通知されます。
(2) テンプレート変数result
の値は、セッションから値を取得する関数です。そしてHTMLテンプレートはリアクティブ・コンテキストなので、セッションに変更があった場合(厳密に言うと、セッション内のmsg
に対応する値が変更された場合)に関数が再実行され、ビューが自動的に更新されます。
(3) 他のリアクティブ・コンテキストの例として、Meteor.autorun
を使用してみました。ここでも関数内でセッションにアクセスしているため、セッションに変更があった場合に関数が再実行されます(コンソールにログが表示されます)。
まとめ
Meteorに限らず、「リアクティブ・プログラミング」というパラダイムは、今後のWeb開発で重要なキーワードになっていくでしょう。
その理由は、WebSocketを使用したサーバプッシュなどが一般的になっていく中で、「バックエンドで行われたデータ変更をビューに反映する」というユースケースが多発すると考えられるからです。そうした際に、「データが変更されるたびに、プログラムの別の箇所と同期する」というコードを書く手間が必要なくなるため、コード量が全体的に削減され、(慣れれば)見通しの良いコードを書くことができます。
次回は、publish/subscribeによるデータ同期とリアクティビティを組み合わせ、サーバ上でのデータ同期をビューに対して自在に反映する方法について学びます。