(この記事はGrimoireJSアドベントカレンダー13日目の記事です。)
こんにちは、GrimoireJS-Coreのコアコミッターmoajoです。
GrimoireJS-Coreは、html
上に埋め込まれたgoml
をパースしてGOM
ツリーを構築し、GomlNode
の相互作用を管理するのが主なお仕事です。
今回はGrimoireJSの根幹であるGomlNode/Componentの細かい仕様についての話です。
新しいノード・コンポーネントを作る
GrimoireJSを拡張するときは、まず機能をComponentとしてモジュール化して、それらをコンポーネントやノードとしてまとめます。
具体的には、
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);
}
});
gr.registerComponent("Component2", {
attributes: {
hugahuga: {
converter: "Number",
defaultValue: 0
}
},
$huga: function (arg) {
console.log(this.getAttribute("hogehoge").Value);
}
});
gr.registerNode("node1", ["Component1"]);
gr.registerNode("node2", ["Component2"], {hugahuga:100}, "node1")
ここではコンポーネントとノードを2つづつ登録してますが、こんな感じでGrimoireInterface
に登録します。
登録はこれで完結してます。つまり、コンポーネント・ノードに必要な情報はすべてここに含まれてます。
コンポーネントの方から仕組みを見てみましょう
Componentの作成
gr.registerComponent
の引数は、先頭から
- コンポーネント名
- コンポーネントのインスタンスかコンストラクタ
- 継承元コンポーネント名
です。
コンポーネント名
コンポーネント名は先頭大文字のCamelCase
が推奨です。基本的にそうしましょう。**ノード名はsnake-case
**です。
コンポーネントの属性
コンポーネントは**attributes
というフィールドで、そのコンポーネントが持つ属性を決めます**。
属性は、
attributes:{
属性名:{
converter:コンバータ名,
defaultValue:デフォルト値
}
}
というフォーマットです。
コンバータは、Number
、String
、Boolean
、Enum
などが最初からありますが、
gr.registerConverter("ConverterName", function(attr,val){
console.log("this attribute is " + attr.name);
console.log("value is " + val);
if(typeof val ==="string"){
return Number(val)
}
if(typeof val ==="number"){
return val;
}
});
という感じで追加できます。受け取った値を適切に変換して返すだけの関数ですね。
gomlで文字列で指定された属性は、属性に指定されたコンバータを通して変換され使われます。
また、コンバータは属性値を設定した時ではなく、取得するときに遅延実行されます。
コンポーネントの$関数
コンポーネントが$で始まる関数を持つとき、それらはGrimoireJSのメッセージレシーバとして扱われます。
GrimoireJSではコンポーネント間でメッセージのやりとりがありますが、それらの対象として扱われるのは先頭に$がつく関数のみです。
この関数内では、this
はこのコンポーネントのインスタンスにバインドされます。
コンポーネントの継承
コンポーネントは継承できます。
継承するとすべての属性と関数が引き継がれます。
また、ノードのgetComponent
関数で継承元コンポーネントを指定したときに継承したコンポーネントも対象になります。つまり、ポリモーフィズムを持つことができます。
GomlNodeの作成
ノードは単純に複数のコンポーネントを入れる箱です。
コンポーネントとそれらの属性の初期値を決めて、タグとしてまとめられます。
gr.registerNode
の引数は、
- ノード名
- デフォルトコンポーネントのリスト
- 属性値
- 継承元コンポーネント名
です。
コンポーネントのリストはそれが最初から入っているというだけです。属性値もコンポーネントのデフォルト値を上書きしてるだけですね。
ノード名
さっきも書きましたが、snake-case
です。
ノードの継承
ノードも継承できますが、ここにポリモーフィズムはありません。
継承によって、コンポーネントリストや属性値を引き継ぎますが、属性値を隠すこともできます。
ノードの継承は、単に追加機能にエイリアスを付けているだけという感じです。
その他
- 属性値の優先順位は、goml>Nodeの初期値>Componentの初期値です。
- コンポーネントやノードの継承関係は、gomlのパース時に評価されます。
- コンバータは、変換できないときは
undefined
を返します。 - また、コンバータにundefinedを通そうとするのは許されず、例外を投げます。
- 従って、属性初期値に
undefined
は設定できません。
- 従って、属性初期値に