Riot.jsとeach属性の愉快な仲間たち
これは Riot.js Advent Calendar 2016 の15日目の記事であり, 煽り記事である.
Riot.js Advent Calendar 2016 - Qiita
また 東京理科大学 Advent Calendar 2016 の14日目の記事でもある.
東京理科大学 Advent Calendar 2016 - Qiita
なお, 私は東京理科大学の学生 ではない のだが, 縁があって『神楽坂一丁目通信局』というサークルに邪魔している. 『神楽坂一丁目通信局』には, ここで紹介するような技術を取り扱っている人間がいる (はずだ) .
Riot.jsは極めて軽量なWebクライアントサイドフレームワークである. 容量で見たらRiot.jsより軽量なのはMithrilぐらいしかない. また, Mithrilと違ってHTML風の構文で記述できる.
私はその小ささと構文が気に入って, 神楽坂一丁目通信局内で利用しているシステムの後継の開発にRiot.jsを使用している.
しかし, 最近Riot.jsの each
属性における不可解な振る舞いに気づいて驚いた.
each
属性とは
each
属性により, 配列をイテレートすることができる.
<p each={a}>
{m}
</p>
<script>
this.a = [{m: "v"}, {m: "w"}];
</script>
これは次のようになる.
<p>
v
</p>
<p>
w
</p>
tagとparent
もう一つ, tagとparent
を紹介する.
<child-tag>
<p><yield><yield></p>
<p>World!</p>
</child-tag>
<parent-tag>
<child-tag>{parent.m}</child-tag>
<p>Riot.js</p>
<script>this.m = 'Hello';</script>
</parent-tag>
これは次のようになる.
<parent-tag>
<child-tag>
<p>Hello</p>
<p>World!</p>
</child-tag>
<p>Riot.js</p>
</parent-tag>
実にわかりやすい.
each
属性の謎, スコープ
では次の例を見てみよう. Riot.jsの記述は非常にわかりやすい. 初心者でもその内容は理解できるはずだ.
<child>
<yield></yield>
</child>
<my-tag>
<div>
<virtual each={a}>
<child>
<h1>parent.message in a tag in a loop</h1>
<p>{parent.message}</p>
<h1>parent.m in a tag in a loop</h1>
<p>{parent.m}</p>
</child>
<div>
<h1>parent.message in a loop</h1>
<p>{parent.message}</p>
<h1>m in a loop</h1>
<p>{m}</p>
</div>
<div>
<h1>message in a loop</h1>
<p>{message}</p>
</div>
</virtual>
<div>
<h1>message in my-tag</h1>
<p>{message}</p>
</div>
</div>
<script>
this.a = [{m: "the value of m"}]
this.message = 'hello there'
</script>
</my-tag>
これは以下のようになる.
<my-tag>
<div>
<child>
<h1>parent.message in a tag in a loop</h1>
<p>hello there</p>
<h1>parent.m in a tag in a loop</h1>
<p>undefined</p>
</child>
<div>
<h1>parent.message in a loop</h1>
<p>hello there</p>
<h1>m in a loop</h1>
<p>the value of m</p>
</div>
<div>
<h1>message in a loop</h1>
<p>hello there</p>
</div>
<div>
<h1>message in my-tag</h1>
<p>hello there</p>
</div>
</div>
</my-tag>
なるほど. ん?
よくよく見てみるとこれはおかしい. どうやらeach
によるループの中ではparent
はmy-tag
を指すらしい.
つまり, ループの中では匿名のtagが生成されていることになる. しかし,
そのtagからはmessage
にアクセスできるし, 子のtagからparent
を見てもイテレートしている値を参照できない.
このtagは一体…?
よく分かる解説
では, この現象を解説しよう. ループ内では匿名のtagが確かに生成されている.
このtagは親のtagから parent
を除く プロパティを継承している. 一方で,
子のtagを作る時にはparent
を匿名でないtagにするようにしているのだ.
ややこしい. 他のフレームワーク使いたくなるなあ. でもどいつもこいつも大抵クソ (偏見) だし, そのうえどれもトラック因数は1人だ. メンテナンス可能性なんてない. 諦観を持とう.