初めてelm-lang(ver0.19)を触る人を対象に分かりやすく解説してみた。 sandbox編
- はじめに
- 準備
- 環境
- 使い方
- Elmアーキテクチャ
- sandbox
- element
- document
- application
- Elmアーキテクチャ
- 解説
- 必要なものをインポート
- main構成
- モデル
- 初期化
- 更新
- type Msg
- update
- 表示
- まとめ
はじめに
Elmは面白い。関数型の独特な雰囲気を持ち、変数不要な不思議なコーディング感覚がまた楽しい。
そんなElmが先日0.19へバージョンアップしました。
以前、Elmを記事にしたが、0.19により、破壊的な変更があり、0.18のソースでは動かなくなりました。
heimdal.hatenablog.com
そこで、Elm0.19用に再入門してみました。 例によってelectron+elmです。
続きを読むPureScript + psc-package + Halogen + Electronでサンプルを動かす
elm+electron 入門 サンプルあり
- はじめに
- 特徴
- 環境
- electronのインストール
- elmのインストール
- コマンド
- elm-repl
- elm-reactor
- elm-make
- elm-package
- パッケージ一覧
- ファイル構成
- サンプルの動かし方
- hello worldサンプル
- カウンタサンプル
- electron特有のバグ
- その他サンプル
- サンプルの動かし方
- まとめ
はじめに
最近純粋関数型プログラミング言語Haskellが気になって少しずつ勉強している。
Haskellを勉強していく中で、elmとpurescriptを知った。 Haskellに近い書き方ができるJavascriptフレームワークで、Web構築できる。 electronと合わせることも出来るので、私と同じように興味持って、これから始める人に参考になれば幸いです。
特徴
AltJSと呼ばれるjavascriptフレームワークであり、記事にしていたMithril.jsとは違い、コンパイルが必要。
記載方法はHaskellのような書き方が出来る。
Elmアーキテクチャとしては、MVCならぬ、MVU(Model-View-Update)を基本としている。
mithril.js v0.2.5のサンプルであるtodoを v1.0.0に書き直してみた
はじめに
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
と書き分けることが出来るようになった。んー、わかりやすい!!!!
- vnodeの統一のおかげで、今までcontrollerで宣言した変数はctrl.hogeのようにかけていたが、今回から
- コンポーネントのラッピング
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
でも参照できるようになった。
- vnode経由でアクセスできるようになった。今までは
- アンマウント防止
- ここはあまり使ったことがないが、説明では、onunload時に
e.preventDefault()
を実行して、アンマウントを防止していたがそれが出来なくなったそうだ。 - 代わりにm.route.set(“/”)と明示的に記載すれば、今まで通りの動きになるという。
- ここはあまり使ったことがないが、説明では、onunload時に
- コンポーネット削除時の処理実行方法
- controllerに
this.onunload
と引っ掛けて処理をしていたが、ライフサイクルが出来たため、それがonremoveと簡単に書けるようになった。
- controllerに
- m.requestの動作
- 描画タイミングでいくつかの動作がサポートされなくなった。その代わりpromise関数チェーンが動作完了すれば、常に再描画することになったようで、困ることはないみたい。
- m.deferred削除
- promiseを使えば解決できるため、削除。
- m.sync削除
Promise.all
と同等の動きであるため、削除。
- xlinkの名前空間が必要になった
- 名前空間が完全サポートされるようになったため、
m("image[xlink:href='image.gif']")
などと明示的に書く必要がある
- 名前空間が完全サポートされるようになったため、
- ネストされた配列
- 1.0からは構造を保持するようになった。
- vnodeのチェック動作
まとめ
と、胡散臭い翻訳と個人的な意見を書いています。
間違いがあれば指摘していただけると幸いです。
翻訳してみて、やはり1.0だけあって、今までの機能がより一層洗練された印象があります。
今までの機能は姿形は変われど、動作は変わっていない部分があるため、追加機能よりも、
まずは動作は変えずに1.0用に書き換えてみたりするのが良いかと思います。
それだけでもソースコードが見やすくなると思います。
そこから、追加機能を試していくとよいかと。
最後に
最後に超簡単な簡単なサンプルを用意しました。
期待しないでください。簡単なhelloworldと入力ボックスがあるだけです。
https://github.com/xheimdal/mithriljs-sample-code
mithril-v1.0を使用すれば良いです。
electron+mithril.jsなので、electronのインストールを忘れずに、
過去の記事を参考にしてもらえば、すぐにできると思います。
では、改めて今年もよろしくお願いいたします。