推奨されるコード構成

Mithrilは、手取り足取りコードを構造化させることはありませんが、アプリケーションの構造として推奨していることがいくつかあります。

経験則として、コントローラがモデルのプロパティを直接変更すべきではない、というものがあります。

データ操作はモデルクラス内で行われるべきです。コントローラが一時的にとはいえ、不完全な状態のモデルを取り扱うべきではありません。

Mithrilの設計としては、エンティティの状態の安定性を鑑みて、エンティティのロジックはモデルレイヤーのアトミックなメソッドで行うことを推奨しています。

抽象化が漏れて、レイヤーを超えてコードが密結合してしまうコードというのはあります。例えばネットワークの非同期性を取り扱う場合にこのようなことがあります。この場合も、エンティティのロジックがコントローラの中に取り残されがちです。

このデザインの原則はDRYと、リレーショナルモデル層のトラックナンバーの大きさから来ています。

これは、他のフレームワークのActiveRecordパターンと大きく異なっています。ActiveRecordパターンのモデルエンティティは、データベースのエンティティのオブジェクト表現という側面が強く、アドホックにコントローラ内でフィード操作されて、最後にsaveメソッドでコミットされます。

Mithrilでは、エンティティのロジックはすべてモデルレイヤ内に置かれるのが推奨されます。エンティティ間の関係を取り扱う必要が出てきたら、コントローラのレイヤーにコードを足すのではなく、新たに専門のモデルレベルのクラスを作ります。

エンティティのリストのフィルタリングや、バリデーションもモデルの責務になります。モデルに定義することで、アプリケーション全体でこれらのメソッドが利用可能になります。

DOM操作は、ビュー内のm()と、configでのみ行うべきです。コントローラで明示的にm.redrawを呼び出すことも出来ますが、操作を抽象化してMithrilの自動再描画システムと統合する方が望ましいです(m.startComputation / m.endComputation参照)。ビューからコントr−ラのクラスのインスタンスを作成するのは避けましょう。


ファイル分割

このサイトのサンプルは、読みやすさのために異なるMVCレイヤーを1つのファイルにまとめていますが、レイヤーごとにファイルを分割するのが推奨されるスタイルです。サンプル:

//app.model.js
var app = {};

app.PageList = function() {
    return m.request({method: "GET", url: "pages.json"});
};

app.vm = {};
app.vm.init = function() {
    this.pages = new app.PageList();
};
//app.controller.js
app.controller = function() {
    app.vm.init();
};
//app.view.js
app.view = function() {
    return app.vm.pages().map(function(page) {
        return m("a", {href: page.url}, page.title);
    });
};

GruntJSのようなタスク自動化ツールを使って、リリース環境向けにファイルを1つにまとめることができます。

一般的に、MVCレイヤーを分割する場合は、モデルレイヤー内で名前空間を定義すべきです。モデルレイヤーは他の全てのレイヤーが依存するからです。

ファイルごとに名前空間を用意するか、あるいはビルドシステムに名前空間を作らせるかは開発者の自由です。

MVCレイヤーそのものでクラスをグループ化するのはやめましょう。model.js、controllers.js、views.jsという名前のファイルは作ってはいけません。

このようなパターンでアプリケーションを分けてしまうと、レイヤー間で不必要なコードの密結合が起きてしまいます。モジュールによる、MVC縦断の縦のつながりがわかりにくくなります。


m.redrawの使用方法

m.redrawを使うと、Mithrilの自動再描画システムのスコープ外から、再描画を行わせることができます。

m.mountm.routeを使っている場合は、このメソッド呼び出しはsetIntervalを使っている場合など、非同期にビューを繰り返し更新したい場合に限定してください。

setTimeoutなどの、繰り返し行われないサービスと統合する場合は、m.startComputation / m.endComputationの方が推奨されます。

m.redrawは、Mithrilの中でもっとも重い処理になりえる関数です。ネイティブのrequestAnimationFrameメソッドが起動されるよりも早いペースで呼び出すべきではありません。このメソッドは、レンダリングに関するコードを実行するのに、ブラウザにとって一番快適なインターバルを提供します。通常は一秒間に60回呼ばれます。

もしこれよりも早いペースで呼び出すと、Mithrilは関数呼び出しを無視して、次のブラウザの再描画のサイクルまで実行を遅らせます。

もし、このメソッドの呼び出しコストがウィンドウの再描画よりも重くなると、フレーム落ちが発生して、アニメーションがなめらかに実行されなくなります。秒間60フレームを維持するために、この関数の単体の呼び出しのコストを16ミリ秒以下に抑えるのは開発者の責任です。

また、Mithrilだけではなく他のテンプレートにも共通することとして、マークアップの複雑性がテンプレートのパフォーマンスに影響を与えます。そのため、テンプレートが大きくなりすぎないようにして、レンダリングを効率よく行うことが大切です。


キーの使用

リストをソートしたり、リストから要素を削除する場合や、スプライスを行う場合は、データとDOM間の参照を維持するために、 key属性を使用してください

キーを使用しなくても期待通りの動作をすることがありますが、再描画のアルゴリズムの効率が下がってコストが高くなる可能性があります。