Help us understand the problem. What is going on with this article?

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の範囲を全てカバーしている。
個人的にはすごく書いていて気持ちが良い。
他のフレームワークと組み合わせるのも簡単なので、覚えておいて損はないフレームワークかなーと思う。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした