Riot.jsに魅せられてしまったemadurandalさんですこんにちは。
Riot.jsいいですよね。Reactから移ってきたのですがもう戻れません。
- 記述がとにかくシンプルかつHTMLセントリック(デザイナーさんにとっても抵抗少ない)。
- HTMLとJavaScriptだけでなく、CSSまでコンポーネントにカプセル化できる。
- サイズが超小さい(モバイルにも優しい)
などなど、メリットは計り知れません。ただ、まだ若いプロジェクトなので、罠もそれなりにありますね。
今回はそんな罠の一つ。表題の件です。
例えばですね。↓こういうのがあったとしましょう。
<my-app>
<my-heavy-ui if={enable} arg={opt.foo} />
this.enable = false;
</my-app>
これ、if=false
なわけですが、実はページ上見えなくなるだけで、実際は<my-heavy-ui>
は実行されてしまいます。取るに足らない小さい部品ならいいんですが、色々処理するような重たい部品だった場合は、嫌ですよね。
それに、再帰的に同じコンポーネントを使いたい場合は、ifで処理を止めようとしても、if=false
でも実行されてしまうために、結局延々とネスト処理が続いてスタックオーバーフローになってしまいます。
<my-folder>
<my-folder if={!isLeaf()} arg={opt.foo} /> ←ifだとうまくいかない!
this.isLeaf = function(){
// 末尾かどうかを判定する処理
}
</my-folder>
では、どうすればいいのか。eachステートメントを使います。
<my-app>
<my-heavy-ui each={enable ? [true]:[]} arg={parent.opt.foo} />
this.enable = false;
</my-app>
<my-folder>
<my-folder each={!isLeaf() ? [true]:[]} arg={parent.opt.foo} />
this.isLeaf = function(){
// 末尾かどうかを判定する処理
}
</my-folder>
どういうことかというと、enable
や!isLeaf()
が真の時は、要素数1(trueとか適当な値が1個入っている)の配列になるため、1個だけコンポーネントが生成されるのです。偽の場合は、空の配列なので、コンポーネントは生成されません。これで、擬似的にifっぽいことをしています。
注意点としては、eachステートメントを使うので、opts.foo
はparent.opts.foo
にしないといけませんね。
これなら、重たい部品が実は生成されていたとか、コンポーネントの再帰がスタックオーバーフローだよウワァァァンとかいうことがなくなります。
私の場合、このやり方をするようにしてから、自作のチャットシステムの表示パフォーマンスがざっと体感で数倍〜10倍くらい速くなりました(ifを多用しまくっていたせいもありますが)。
特に、IE系はパフォーマンスの改善が顕著です。みなさま、騙されたと思ってぜひお試しを!
しかし、このifの挙動、なんなんですかね。Riot.jsのソース読んだり、Githubコミュニティに入り浸っているほど、深く足を突っ込んでいるわけでもないので、よくわかりません。
めんどくさいので、将来のバージョンでは仕様を変えて欲しいところですね。
2015/10/26追記
Riot.js公式サイトの日本語翻訳プロジェクトを主導され、ikki などのRiot拡張なども手がけていらっしゃる、国内におけるRiotの第一人者 @cognitom さんにこのifの挙動について質問したところ、こんなお答えがありました。
@emadurandal 経緯についてはこちらをどぞ。 https://t.co/xLZpdf1kHL
— Tsutomu Kawamura (@cognitom) 2015, 10月 26
なるほど、そうでしたか……。
実装上のやむない理由により、今のような仕様になっているのですね。もちろん、本来のifの役割として望ましい挙動でないことは開発の方々も認識されているわけなので、いずれ解決されることを期待したいですね。
それでは、皆様もよいRiot.jsライフを!^o^/