GrimoireJSのメッセージレシーバ

(この記事は[GrimoireJSアドベントカレンダー](http://qiita.com/advent-calendar/2016/grimoire)20日目の記事です。)

こんにちは。最近GrimoireJS-Coreの最適化を頑張っているmoajoです。

前回までは、[GrimoireJSの仕組み](http://qiita.com/moajo/items/f6cb9cfeec6bbb31ddb0)や、[かんたんなプラグインの作り方](http://qiita.com/moajo/items/47547bab7cc7c0012359)の記事を書いたりしましたが、今回は特に、**コンポーネントのメッセージング**に関して詳しく書いてみたいと思います。

# メッセージって？
毎度のことですが、コンポーネントの追加のはこんなコードでやります。
[そもそもコンポーネントってなに？](http://qiita.com/moajo/items/f6cb9cfeec6bbb31ddb0#component)というのは前々回でしたね。

```javascript:index.js
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`**フィールドが必要です。
この場合、

```xml
<Component1 hogehoge="hogeValue"/>
```
という感じのタグが使えるようになります。このあたりも[名前とコンバータを指定するだけ](http://qiita.com/moajo/items/47547bab7cc7c0012359#%E3%82%B3%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%8D%E3%83%B3%E3%83%88%E3%81%AE%E5%B1%9E%E6%80%A7)なのでらくらくですね。

話がそれましたが、今回のメインは`$mount`などの**先頭にドルがつく名前のfunction**です。
これらは、**メッセージレシーバ**と呼ばれます。

##仕組み
![message_hoge.png](https://qiita-image-store.s3.amazonaws.com/0/116979/7180ef29-2889-e6ff-7afc-32916374d99f.png)
メッセージレシーバは読んで字のごとく**メッセージを受け取る関数**です。
メッセージは、外部からノードを経由して飛んで来ます。
メッセージの実態は単に関数名を示す文字列で、同名で**先頭に$がつく関数**
をコンポーネントから探し出し、呼び出します。
ちなみに、メッセージを送るときにはオブジェクトを一緒に送ることができて、それは関数に引数として渡されます。
メッセージの仕組み自体が一種の抽象化層となって、**コンポーネント同士の安全で疎結合な連携**を可能にするんですね。

##sendとbroadcast
![broadcastMessage.png](https://qiita-image-store.s3.amazonaws.com/0/116979/45e6e3e6-261b-e60c-3914-1da2e7117689.png)
さっきの図では`hoge-node-b`に対して`sendMessage('hoge')`が呼び出された結果、`hoge-node-b`のコンポーネントそれぞれの`$hoge`関数があれば呼び出されました。
でも、この方法では**対象となるノードに一つ一つ`sendMessage('hoge')`しないといけません**。場合によっては不便ですね。
そんなときは、`broadcastMessage('hoge')`を使いましょう。使い方は`sendMessage('hoge')`と変わりません。
違いは、**そのノードの子要素にもすべて`broadcastMessage('hoge')`が呼び出される**ことです。
たとえば、ツリーのルートノード(普通は`<goml>`)に対して`broadcastMessage('hoge')`すると、ツリー上のすべてのノードに`sendMessage('hoge')`したのと同じことになります。



## メッセージを受け取る
実際にメッセージを受け取って処理をしてみましょう！
とおもいきや、**もう既に受け取っています。**

```javascript:index.js
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`はロード完了時に[自動的に送られるメッセージ](http://qiita.com/moajo/items/c453b64883f006cc709c#%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E3%83%A1%E3%83%83%E3%82%BB%E3%83%BC%E3%82%B8)です。
このコンポーネントがアタッチされたノードに、`sendMessage('hoge')`とすると、コンソールにノードの`hoge`属性の値が表示されます。

##メッセージを送る
送るのはもっと簡単です。

```js:index.js
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

などがあります。
基本的に呼ばれないことがあると不都合があるようなシステムメッセージが含まれます。