heimdalの技術ノート

IT全般、Linux、Windows、プログラミング開発など、何でもござれ

mithril.js v0.2.5のサンプルであるtodoを v1.0.0に書き直してみた

f:id:xheimdal:20170208221535p:plain

はじめに

mithril.jsの公式サイトにサンプルとしてtodoアプリがある。
1.0.0になってそのサンプルがなくなっていたので、独自で作り変えてみる。
なるべく公式のサンプルのソースを残したまま実装してみる。

http://mithril-ja.js.org/getting-started.html

環境

electron 1.4.15
mithril 1.0.0

ソースコード

早速ソースコードを晒してみる。

https://github.com/xheimdal/mithriljs-sample-code/tree/master/todo-v1.0.0

mithril 0.2.5のソースと見比べながら見てほしい。

const todo = {
    //TodoListクラスはtodoの配列
    list: new Array,
    //Todoの内容を保持する変数
    description: stream(""),
    //リストのモデルを作成
    listModel: function(data){
        this.description = stream(data.description);
        this.done        = stream(false);
    },
    //初回処理
    oninit: function(vnode) {
        //Todo追加処理
        vnode.state.add = () => {
            return function() {
                if (vnode.state.description()) {
                    vnode.state.list.push(new vnode.state.listModel({description: vnode.state.description()}));
                    vnode.state.description("");
                }
            }
        };
    },
    //ビュー
    view: function(vnode) {
        return m("html", [
            m("body", [
                m("input", {onchange: m.withAttr("value", vnode.state.description), value: vnode.state.description}),
                m("button", {onclick : vnode.state.add()}, "追加"),
                m("table", [
                    vnode.state.list.map(function(task, index) {
                        return m("tr", [
                            m("td", [
                                m("input[type=checkbox]", {onclick: m.withAttr("checked", task.done), checked: task.done()})
                            ]),
                            m("td", {style: {textDecoration: task.done() ?"line-through" : "none"}}, task.description()),
                        ])
                    })
                ])
            ])
        ]);
    }
}

m.mount(document.getElementById("root"), todo);

内容

公式のサンプルではMVCモデルを意識して作られており、役割分担がわかりやすかった。
今回、書き換えた際は、MVCを考えずに書き換えた。
理由としては、1.0.0からコンポーネント内にvnodeが使えるようになったため、できる限りvnode内で処理してみたかったから。
oninitやviewはそれぞれ機能を持っていて、それらの関数の引数をvnodeと書いている。
listやlistModelなどコンポーネント内で宣言したものは、vnode.state.listなどのように書くことで参照することができる。
これが中々便利で、関数も第一引数のみ書けば、vnode上の情報を色々参照できる。
今回は各引数をvnodeと意味ありげに書いているが、わかりやすくするための例であり実際はv.state.listやval.state.fugaなど任意の名前で書いてよいです。

※始めはvnodeという引数の名前に特別な意味があると勘違いしていたなんて、恥ずかしくて言えない。。。orz

また、vnode.stateと書くのが長いので、こんな感じに短縮して書いた方がいいかもしれない。

    //初回処理
    oninit: function(v) {
        let vs = v.state
        //Todo追加処理
        vs.add = () => {
            return function() {
                if (vs.description()) {
                    vs.list.push(new vs.listModel({description: vs.description()}));
                    vs.description("");
                }
            }
        };
    },
    //ビュー
    view: function(v) {
        let vs = v.state
        return m("html", [
            m("body", [
                m("input", {onchange: m.withAttr("value", vs.description), value: vs.description}),
                m("button", {onclick : vs.add()}, "add"),
                m("table", [
                    vs.list.map(function(task, index) {
                        return m("tr", [
                            m("td", [
                                m("input[type=checkbox]", {onclick: m.withAttr("checked", task.done), checked: task.done()})
                            ]),
                            m("td", {style: {textDecoration: task.done() ?"line-through" : "none"}}, task.description()),
                        ])
                    })
                ])
            ])
        ]);
    }

まとめ

今までMVCを意識しながら書いていたが、mithril.jsのvnodeの特徴を活かしたコーディングをしてみた。
出来上がったものは、思ったよりすっきりと、まとまったソースが出来たと思う。
ただ何でもかんでもコンポーネント内に宣言してもいいのかという懸念点があり、さじ加減はわからない。
出来上がったソースの可読性が高ければ、何でもいいかもしれない。

また今回使っていないが、ライフサイクルメソッドであるoncreateやonupdateを使えばより柔軟に状態ごとの処理が書けるようになる。
まだうまく扱えきれていないが、出来る人が書けば、うまく活用できると思う。

今日はここまで。引き続き色々サンプルを届けていくので、乞うご期待!

mithril.js v1.0が出たぞ!!!ついでに変更点を紹介してみた。

はじめに

明けましておめでとうございます。
今年もよろしくお願いいたします。 久しぶりの更新です。
表題の通り、mithril.js v1.0が遂にリリースされました。
もうワクワクが止まりません!
色々機能が増えたり、動作が変わっていたりしているので、変更点を紹介してみます。

変更点

ここを参照
Change log - Mithril.js

  • m.propが削除され、streamに変更へ
    • ここはどうなるか心配していましたが、セッターゲッターのシンプルな動作はそのままで、より高性能になった。
    • 例えば、エクセルのセル数式のように、文字列を繋げたり、A1 + B1 = C1のような数字の合計表示しA1が更新されるとC1の結果も変わるようなことなど、応用が利くようになったみたい。
  • m.componentの削除
    • m()で出来るので、特に影響はない。
  • config機能拡張
    • configは後処理のみだったが、domを生成する前、後、などのライフサイクルが出来、より細かく指定できるようになった。
    • 合わせてコンポーネントのcontrollerではなく、ライフサイクルの同じ動作をするoninitを指定することになった。
    • m.routeもconfigを使っていたが、oninitやoncreateと書き換える必要がある。
  • 再描画システムの動作が変更
    • 読み間違いでなければ、今までシステムが独自のタイミングで再描画を判定していたが、不具合も多かったため、原則常に再描画してくれるようになった。
    • m.startComputation()m.endComputation()もv1.0になるタイミングで機能削除。
    • 再描画したくない場合は、イベントオブジェクトに対して、e.redraw = falseを記載すればいいとのこと。
  • コンポーネントの引数の変更
    • 引数がvnodeというオブジェクトを統一になった。
    • 引数(hoge)を渡した場合は、vnode.attrs.hogeと書くことで、参照することができる。書くのが少し面倒になった分、わかりやすくなったかもしれない。
  • viewの引数の渡し方の変更
    • vnodeの統一のおかげで、今までcontrollerで宣言した変数はctrl.hogeのようにかけていたが、今回からvnode.state.hogeのように書くことになった。
    • oninitやoncreateとライフサイクル毎に宣言したものが、vnode.stateになったという意味となる。
    • つまり、外部からの引数は、vnode.attrs.hogeと書き、内部の変数は、vnode.state.fugaと書き分けることが出来るようになった。んー、わかりやすい!!!!
  • コンポーネントのラッピング
    • m("div", component)と昔書けていたのを、書き方統一のために、m("div", m(component))と常に書くようになった。
    • ここはm()で包まなくてもいいの?という疑問がなくなる分、良いと思う。
  • m.mount() と m.route()を実行する際の第二引数は、vnode経由になった
  • m.route.mode
    • ラウトモードが書きやすくなり、m.route.mode = "pathname"m.route.mode = "search"m.route.prefix("")m.route.prefix("?")と書けるように。他にも機能があるみたい。
  • m.routeの読み書きの機能が分断
    • m.route.get()m.route.set("/other/route")と書き方が変わった。
  • ラウトパラメータの参照方法追加
    • vnode経由でアクセスできるようになった。今まではm.route.param("attr")だったところをvnode.attrs.attrでも参照できるようになった。
  • アンマウント防止
    • ここはあまり使ったことがないが、説明では、onunload時にe.preventDefault()を実行して、アンマウントを防止していたがそれが出来なくなったそうだ。
    • 代わりにm.route.set(“/”)と明示的に記載すれば、今まで通りの動きになるという。
  • コンポーネット削除時の処理実行方法
    • controllerにthis.onunloadと引っ掛けて処理をしていたが、ライフサイクルが出来たため、それがonremoveと簡単に書けるようになった。
  • m.requestの動作
    • 描画タイミングでいくつかの動作がサポートされなくなった。その代わりpromise関数チェーンが動作完了すれば、常に再描画することになったようで、困ることはないみたい。
  • m.deferred削除
    • promiseを使えば解決できるため、削除。
  • m.sync削除
    • Promise.allと同等の動きであるため、削除。
  • xlink名前空間が必要になった
    • 名前空間が完全サポートされるようになったため、m("image[xlink:href='image.gif']")などと明示的に書く必要がある
  • ネストされた配列
    • 1.0からは構造を保持するようになった。
  • vnodeのチェック動作
    • view外部でコンポーネットのインスタンスを作成してしまうと、vnodeがコンポーネントのチェックが出来なくなり、トリガーなどが動かなくなる場合がある。
    • チェックの動作を正常に動かしたい場合は、コンポーネットはviewで呼び出すようにする必要がある。

まとめ

と、胡散臭い翻訳と個人的な意見を書いています。
間違いがあれば指摘していただけると幸いです。

翻訳してみて、やはり1.0だけあって、今までの機能がより一層洗練された印象があります。
今までの機能は姿形は変われど、動作は変わっていない部分があるため、追加機能よりも、
まずは動作は変えずに1.0用に書き換えてみたりするのが良いかと思います。
それだけでもソースコードが見やすくなると思います。
そこから、追加機能を試していくとよいかと。

最後に

最後に超簡単な簡単なサンプルを用意しました。
期待しないでください。簡単なhelloworldと入力ボックスがあるだけです。
https://github.com/xheimdal/mithriljs-sample-code
mithril-v1.0を使用すれば良いです。
electron+mithril.jsなので、electronのインストールを忘れずに、
過去の記事を参考にしてもらえば、すぐにできると思います。

heimdal.hatenablog.com

では、改めて今年もよろしくお願いいたします。

electronを使ってmithril.js 超入門 m.route編

  • はじめに
  • 準備
  • 環境
  • m.route
  • パターン1
  • パターン2
  • パターン3
  • 最後に

はじめに

m.routeについて、何パターンか紹介する。

準備


以前の記事からサンプルを持ってきてもいいが、
場所を用意したので、ここからダウンロードした方が早い。
https://github.com/xheimdal/mithriljs-sample-code
hello-mithrilを使用すれば良い。

続きを読む

electronを使ってmithril.js 超入門 m.prop m.withAttr編

  • はじめに
  • m.propとは
  • m.withAttrとは
  • 準備
  • パターン1
  • パターン2
  • パターン3
  • パターン4
  • 最後に

はじめに

mithril.jsのAPIの中にm.propがあるが、それを使うパターンを紹介する。

m.propとは

mithril.js版のgetter/setter関数のAPIである。
変数に代入することで、getter/setter関数用のプロパティを作成する。

続きを読む

electronを使ってmithril.js 超入門

  • はじめに
  • 準備
    • ファイルの中身
  • 事前知識
  • パターン
    • その1
      • 説明
    • その2
      • 説明
    • その3
      • 説明
    • その4
      • 説明
  • 最後に

はじめに

heimdal.hatenablog.com

前回の記事からの続き。 mithril.jsのhelloworldのパターンを書いていく。

続きを読む