(この記事はGrimoireJSアドベントカレンダー20日目の記事です。)
こんにちは。最近GrimoireJS-Coreの最適化を頑張っているmoajoです。
前回までは、GrimoireJSの仕組みや、かんたんなプラグインの作り方の記事を書いたりしましたが、今回は特に、コンポーネントのメッセージングに関して詳しく書いてみたいと思います。
メッセージって?
毎度のことですが、コンポーネントの追加のはこんなコードでやります。
そもそもコンポーネントってなに?というのは前々回でしたね。
gr.registerComponent("Component1", {
attributes: {
hogehoge: {
converter: "String",
defaultValue: null
}
},
$mount: function (arg) {
console.log("mount");
},
$awake: function (arg) {
console.log("awake");
},
$hoge: function (arg) {
console.log(hoge);
}
});
コンポーネントと言っても実際ただのオブジェクトです。
ただ、goml
に書ける属性を持たせたいなら**attributes
**フィールドが必要です。
この場合、
<Component1 hogehoge="hogeValue"/>
という感じのタグが使えるようになります。このあたりも名前とコンバータを指定するだけなのでらくらくですね。
話がそれましたが、今回のメインは$mount
などの先頭にドルがつく名前のfunctionです。
これらは、メッセージレシーバと呼ばれます。
##仕組み
メッセージレシーバは読んで字のごとくメッセージを受け取る関数です。
メッセージは、外部からノードを経由して飛んで来ます。
メッセージの実態は単に関数名を示す文字列で、同名で先頭に$がつく関数
をコンポーネントから探し出し、呼び出します。
ちなみに、メッセージを送るときにはオブジェクトを一緒に送ることができて、それは関数に引数として渡されます。
メッセージの仕組み自体が一種の抽象化層となって、コンポーネント同士の安全で疎結合な連携を可能にするんですね。
##sendとbroadcast
さっきの図ではhoge-node-b
に対してsendMessage('hoge')
が呼び出された結果、hoge-node-b
のコンポーネントそれぞれの$hoge
関数があれば呼び出されました。
でも、この方法では対象となるノードに一つ一つsendMessage('hoge')
しないといけません。場合によっては不便ですね。
そんなときは、broadcastMessage('hoge')
を使いましょう。使い方はsendMessage('hoge')
と変わりません。
違いは、そのノードの子要素にもすべてbroadcastMessage('hoge')
が呼び出されることです。
たとえば、ツリーのルートノード(普通は<goml>
)に対してbroadcastMessage('hoge')
すると、ツリー上のすべてのノードにsendMessage('hoge')
したのと同じことになります。
メッセージを受け取る
実際にメッセージを受け取って処理をしてみましょう!
とおもいきや、もう既に受け取っています。
gr.registerComponent("Component1", {
attributes: {
hogehoge: {
converter: "String",
defaultValue: null
}
},
$mount: function (arg) {
console.log("mount");
},
$awake: function (arg) {
console.log("awake");
},
$hoge: function (arg) {
console.log(hoge);
}
});
> awake
> mount
そのまんまですね。awake
,mount
はロード完了時に自動的に送られるメッセージです。
このコンポーネントがアタッチされたノードに、sendMessage('hoge')
とすると、コンソールにノードのhoge
属性の値が表示されます。
##メッセージを送る
送るのはもっと簡単です。
gr("#main")("mesh").sendMessage("hoge");
これで、すべてのmesh
タグにsendMessage('hoge')
できます。
##enabled
ノードやコンポーネントには、enabled
というフィールドがあります。
有効・無効を切り替える設定です。
コンポーネントが無効(enabled:false
)だと、メッセージを受信しなくなります。
また、ノードが無効だと、すべてのコンポーネントが無効であるように振る舞います。
さらに、broadcaseMessage
を子ノードに伝達しなくなります。
とくに注意するのは、ノード自体が有効でも親が無効であれば、実質的に無効であるように見えることです。
つまり、コンポーネントが本当に有効なのは、先祖ノードがすべて有効なときだけです。
この状態は、GomlNode#isActive()
で確認できます。
システムメッセージ
メッセージは基本的にコンポーネント同士がノードを介して情報の受け渡しをするときに使われますが、幾つかのメッセージはコンポーネントからではなく、GrimoireJS-Coreから直接送られてきます。
現状では以下のものがあります。
$awake
コンポーネントが最初にmount
メッセージを受け取る直前に一度だけ呼ばれます。
そのコンポーネントのインスタンスで最初に一度だけすべき処理はここでしましょう。
$mount
コンポーネントがツリー上でアクティブになったときに呼ばれます。
タイミングが微妙にわかりにくいですが、具体例を挙げると
- enabledな状態で、アクティブなノードの子ノードに追加されたとき
- アクティブなノードの子ノードにいる状態で、disableからenableに変化したとき
- gomlをロードした直後(goml上でdisableでなければ)
などです。
コンポーネントはノードごとツリー上での位置を移動される場合があるので、移動後に有効化されたら最初に呼ばれると思っておけば大体正しい認識です。
ここでは、ツリー上の位置や環境に依存する初期化などを行います。
##$unmount
$mount
の逆です。ツリーから外れたときに呼ばれます。
##$dispose
コンポーネントが廃棄される直前に呼ばれます。
後片付けをしましょう。
これ以降メッセージは一切受信しません。
##$treeInitialized
少し特殊ですが、ページロード時にgomlがパースされて初期ツリーが構築され、全ノードがmountされた後に呼ばれます。
ツリー全体が完全に初期化された状態で呼ばれる最初のメッセージです。
##無効状態でもメッセージを受信する
さっき、コンポーネントが無効なときは、メッセージを受信しなくなると書きました。
あれは嘘です。
厳密に言えば、特定のシステムメッセージは、有効無効にかかわらず受信します。
それ以外の、ユーザが投げるメッセージは例外なく受信されません。
enabled
を無視するメッセージは、
- awake
- unmount
などがあります。
基本的に呼ばれないことがあると不都合があるようなシステムメッセージが含まれます。