本稿は日本語記事がまだ少ないMithrilの基本機能の紹介を目的とした「基礎編」として、簡単なサンプルアプリを基に導入方法や主要メソッドを解説していきます。執筆時点のバージョンであるv0.2.0の内容です。
Mithrilとは
Mithrilは、VirtualDOMを採用したデータバインディング型JavaScriptフレームワークです。軽量、高速をコンセプトに作られているため、モバイル向けのシングルページアプリケーションに最適なフレームワークと言えます。
データバインディング型フレームワークで生じる、DOMの更新を最小限にとどめるための仕組みです。JSオブジェクトなどを使い仮想的にDOMのツリー構造を作成し、変更差分を算出する仕様のため、高度なDOMの処理を自前で行わなくても、最適化された差分計算の仕組みによりDOMが更新されます。細かい実装方法はフレームワークによって異なりますが、レンダリングパフォーマンスが向上するメリットがあります。
日本ではVirtualDOM Advent Calendar 2014がキッカケとなり、「VirtualDOM」対応のJavaScript Frameworkが一般的にも注目され始めてきました。代表的なフレームワークはReact、mercury、Elmです。2015年5月現在ではReactが人気です。
Mithrilの特徴
- 軽量(minifyで19KB)
- 高速(mercury-perfで高いパフォーマンスはでている)
- APIがシンプル(APIは15個と少なめ。主要APIは5つ)
- レンダリング後にサードパーティ製ライブラリを適応可能
- JSXのようにDOM形式でViewを書ける、MSXというコンパイラも用意されている
- コンポーネント形式でのViewの管理が可能
- 対象ブラウザはIE9以上
Mithrilのパフォーマンス
Mithril公式サイトで発表されているパフォーマンステスト結果は上記となっています。mercury-perfでも高いパフォーマンスが出ています。
ただし、フレームワークごとに実装方法が異なる都合上、得意な処理と不得意な処理があるため、環境や処理内容によってはMithrilが遅いこともあります。そのため、総合点としては優秀な部類であるという認識程度でとどめていただいたほうが良いと言えます。
Mithrilの実装例
今回は、簡単なサンプルコードをもとに各メソッドについて説明します。下記のようなTodoアプリケーションを作成するためのサンプルコードです。
<!DOCTYPE html>
<html dir="ltr" prefix="fb: http://www.facebook.com/2008/fbml">
<head>
<meta charset="UTF-8">
<script src="mithril.js"></script>
</head>
<body>
<div id="contents"></div>
<script>
/*
* Modelを定義
* ページ内の文言データを管理
*/
var PageModel = function() {
this.data = m.prop({});
this.fetch = function(){
var that = this;
m.request({method: "GET", url: "page.json"}).then(function(resp){
that.data(resp);
});
return this;
};
this.get = function(){
return this.data;
};
};
/*
* Modelを定義
* Todoのアイテムデータ管理
*/
var ItemModel = function() {
this.data = m.prop({});
this.get = function(){
return this.data;
};
this.update = function(data){
this.data(data);
return this;
};
};
/*
* Modelを定義
* Todoのリストデータを管理
*/
var ListModel = function() {
this.data = [];
this.get = function(){
return this.data;
};
this.add = function(item){
if(!item.text && item.text === ""){
return false;
}
this.get().push(item);
};
};
/*
* controller定義
*/
var myCtrl = function() {
var that = this;
//pageModel
var pageModel = new PageModel().fetch();
this.pageData = pageModel.get();
//listModel
var listModel = new ListModel();
this.listData = listModel.get();
//itemModel
var itemModel = new ItemModel();
this.itemData = itemModel.get();
//inputの"onchange"時に実行する関数
this.onChangeInput = function(value){
itemModel.update({ text: value });
};
//追加ボタンの"onclick"時に実行する関数
this.onSubmit = function(){
listModel.add(that.itemData());
};
};
/*
* viewを定義
*/
var myView = function(ctrl) {
return [
m("h1", ctrl.pageData().title),
m("p", ctrl.pageData().description),
m("br"),
m("hr"),
m("h2", "Todo登録"),
m("div",[
m("input", {onchange: m.withAttr("value", ctrl.onChangeInput)}),
m("button", {onclick: m.withAttr("value", ctrl.onSubmit)}, "追加")
]),
m("br"),
m("hr"),
m("h2", "Todo一覧"),
m('ul', [
ctrl.listData.map(function(item, index) {
return m("li", item.text);
})
])
];
};
//レンダリング開始
m.mount(document.getElementById("contents"), {
controller: myCtrl,
view: myView
});
</script>
</body>
</html>

