VirtualDom
Mithril.js

Mithril.jsで画像のアップロード、プレビューを実装してみて分かったこと

More than 3 years have passed since last update.


はじめに

最近Mithril.jsで遊んでいる。

Mithril.jsは最近流行りのReactと同じ仮想DOMな実装のjsフレームワーク。

とにかく速度が爆速で、Reactの5倍速いらしい。

↓の記事がすごく分かりやすい。(僕がやってみようと思ったきっかけもこの記事でした。)

最速MVCフレームワークMithril.jsの速度の秘密 - Qiita

上記の記事にも書いてあるけど、Mithrilはredrawするタイミングが少し特殊で、それが高速化の一端を担っている。

ブラウザから画像をアップロードして、プレビューする実装をしてみて、その辺が理解できたので紹介。


環境


  • Mithril.js 0.2


資料

auto-redrawのタイミングに関する公式ドキュメント


コード

http://jsfiddle.net/k0Lu22fm/2/

imageView = {}

imageView.vm =
init: ->
@image = m.prop(null)
@fileChange = (e) =>
file = e.target.files[0]
unless file?
@image(null)
return
fr = new FileReader
fr.readAsDataURL(file)
m.startComputation()
fr.onload = (event) =>
@image(event.target.result)
m.endComputation()

imageView.controller = -> imageView.vm.init()

imageView.view = ->
m('div', [
m('div', [
m('input[type=file]', {onchange: imageView.vm.fileChange})
])
m('img', src: imageView.vm.image()) if imageView.vm.image()?
])

m.mount(document.getElementById('image-view'), {controller: imageView.controller, view: imageView.view})


解説

Mithrilは、デフォルトではcontrollerのinitializeが走った時か、イベントが発火した時にauto-redrawされる。

Reactではstateという概念があって、setStateメソッドで状態が変更された時に再renderが走るわけだけど、Mithrilのプロパティ(m.prop)にはそういう仕組みはない。

m.propは、純粋にgetter、setterを定義してくれるだけ。

デフォルトでイベント発火時にauto-redrawされるので、viewからイベントを発火してイベント中にプロパティを更新するだけなら、イベント終了のタイミングでredrawしてくれる。

だけど今回はHTML5のFileAPIのFileReaderを使って、非同期にファイル読み込みを行って、プロパティを更新するようにしたい。

そうすると、イベントが終了した時にはまだ読み込みがされていないので、そのままではredrawされない。

そこで登場するのが、m.startComputation()m.endComputation()だ。

これが何かを説明する前に、Mithrilのredrawが走るタイミングを説明する。

Mithrilでは内部カウンタを持っていて、イベントハンドラが呼ばれるとincrementされて、終了時にdecrimentされる。redrawが走るのはこの内部カウンタが0になった時だ。

m.startComputation()は内部カウンタを手動でincrementするメソッドで、m.endComputation()はdecrementする。

つまり、イベント発火時にincrementされて1になった内部カウンタは、さらにm.startComputation()を呼ばれて2になる。

イベント終了時にdecrementされて1になり、FileReaderのonloadイベントでm.endComputation()が呼ばれて0になる。

ここで0になったのが検知されて、redrawが走る。

この仕組みはすごくよくできていて、viewメソッドにconsole.logを仕込むと分かるけど、viewメソッドはファイルアップロード時に1度しか呼ばれない。

非同期処理が複数あって、全て終了した時にredrawしたい、みたいな時にはそれぞれのメソッドでm.startComputation()m.endComputation()を呼ぶようにしておけば、無駄なredrawでリソースを消費しなくて済む。(公式に分かりやすい例が書いてあります。)

m.render()メソッドを使えば、強制的にredrawすることも可能。


所感

今回はViewの部分だけだったけど、Mithril.jsは軽量な割に、MVCの範囲を全てカバーしている。

個人的にはすごく書いていて気持ちが良い。

他のフレームワークと組み合わせるのも簡単なので、覚えておいて損はないフレームワークかなーと思う。