Webアプリを作る際に、
フレームワークとか呼ばれるライブラリを使うと
それなりに便利です。
MVC(Model-View-Controller)を採用していることが多いかと思われます。
JavaScriptなフレームワークの1つに
Mithril.jsというのがあります。
仮想DOMを導入しているフレームワークです。
そんなにメジャーではないようですが、
個人的には割と良さげな印象を持ってます。
そんなMithrilですが、
2017年2月くらい(?)にバージョンが 1.x になったようです。
ただし、APIが改新されて 0.2.x との後方互換性が無くなっています。
やれやれ、APIを学習し直さないといけないのか・・・
とか思いましたが、
こちらのKey conceptsとか読んだら、考えが変わりました。
ざっくり、どう変わったかと言えば、
アンチパターン回避の精神を推し進め、
MVCのうちのViewに重点を置き、
仮想DOMをより洗練させた。
といった感じでしょうか。
フレームワークを使う場合、
設計者の意図するやり方に従う必要があります。
学習する手間はありますが、
慣れてくれば道に沿って進んで行けばよくなります。
一方、それは規約になり、好きなようにはやれなくなります。
機能が増えれば複雑性も増すかもしれません。
Mithril v1.x はその辺を嫌っているようです。
より簡素に、また機能を集約し、
アンチパターン回避を促すべく、
ModelとControllerに縛りを設けないで、
柔軟にうまくやれるよ。
ということを目指しているようです。
さて、
v0.2.xに比べてv1.xはどう変わったのかについて、
こちらのChange-Logを参照しつつ、
大きく変わったと思われる部分を中心に
詳しく見てみたいと思います。
- m.prop()の削除。
- ModelとViewの間で双方向にバインディングするための機能ですが、
MVCの縛りは止めたので削除となったようです。
とは言え、それなりに便利な場合もあります。
その辺を考慮してか、
mithril/stream
という代替ライブラリがオプションで用意されています。
ただ、それなりに高機能になってる感じなので、
単純にsetter/getterな機能だけがほしい場合は、
v0.2.xからコードを借りてきた方が早いかも。
↓こんな感じに。function prop(store) { // simple getter-setter var p = function() { if (arguments.length) store = arguments[0]; return store; }; p.toJSON = function() { return store; }; return p; }
- m.component()の削除。
- 従来は View + Controller でコンポーネントでしたが、
縛りを無くしたのでViewだけで構成できるようになりました。
仮想DOMを記述するためのm()
に統合されたので、
削除となったようです。
コンポーネント中にコンポーネントを記述できるようになったので、
柔軟性が増したかも。 - config機能を改善してライフサイクルなイベントを導入。
-
config
は例えばcanvasの描画更新とかで使えたりしましたが、
より詳細に柔軟に対応できるように Lifecycle methods が導入されました。
従来Contorllerでやってた初期化はoninit()
でやれるようになったり、
例えばoncreate()
でcanvasのコンテキストを取得しておいて、
onupdate()
で描画を更新とか細かくやれるようになりました。
仮想DOMだけでなくコンポーネントに対しても設定可能です。m("div", { // Called before the DOM node is created oninit : function(vnode) { /*...*/ }, // Called after the DOM node is created oncreate : function(vnode) { /*...*/ }, // Called before the node is updated, return false to cancel onbeforeupdate : function(vnode, old) { /*...*/ }, // Called after the node is updated onupdate : function(vnode) { /*...*/ }, // Called before the node is removed, return a Promise that resolves when // ready for the node to be removed from the DOM onbeforeremove : function(vnode) { /*...*/ }, // Called before the node is removed, but after onbeforeremove calls done() onremove : function(vnode) { /*...*/ } })
- 再描画の挙動を変更。
- Mithrilでは再描画は自動で行われるので、
あまり気にする必要は無いのですが、
明示的に再描画を抑制したい場合があります。
従来は関数で制御していましたが、
結局イベントの延長で対処することになるので、
イベントのパラメータとして設定できるようになりました。
確かにこの方がスマートかも。m("div", { onclick : function(e) { e.redraw = false } })
m.redraw()
による再描画の挙動が、
即時に同期的に行う方式から、
予約して次のrequestAnimationFrame
のタイミングで
非同期に行うように変わりました。
むやみにm.redraw
したとしても無駄な描画は起きなくなりますね。
また従来は、
m.startComputation
/m.endComputation
を使うことで
非同期処理が完了するまで描画を遅延させることができましたが、
アンチパターンの恐れを考慮して削除されました。
非同期処理後の再描画はm.redraw()
を活用することになりそうです。 - view()の引数やコンポーネントに対する引数の変更。
- viewの引数がcontrollerからvnodeに変わりました。
外部から渡すことになるコンポーネントに対する引数が、
オプション扱いからvnode.attrs
となりました。
さらにvnode.state
を使うことで、
コンポーネント内部の状態を管理できるようになりました。
外部からのがattrs
で内部のがstate
になるので分かりやすいですね。m.mount(document.body, { oninit: function(vnode) { vnode.state.something = ... }, view: function(vnode) { // Use vnode.state instead of ctrl // Use vnode.attrs instead of options } })
- m.deferredとm.syncを削除。
- いずれも非同期処理を扱うために用意されたものですが、
既に多くのブラウザでPromise対応が進んでいるので、
そっちに合わせます。ということで削除になった模様です。
非対応なブラウザであってもpolyfillで代替されるので問題ないです。
各ブラウザでのES6対応はどのくらい進んでいるんだろ、
と思ってここを参照してみたら、
IE11を無視すれば概ね対応が済んでるっぽい感じですね。
その他にもいろいろありますが、
長文になったのでこの辺にしておきます(^_^;)
だいたい押さえたいところは書いたつもりです。
ちなみに、
Mithril.jsを使って作ったのがこれ。