Vue.jsをjs_of_ocamlから使う方法
Vue.jsとはJavascriptでMVVMするための軽量なフレームワークです。
ですっていうか3日前に知ってこれはいいやと思い、js_of_ocamlから使う方法を考えていました。
ちなみに自分のJavascriptのレベルはほとんど0です。「Objectに動的にメソッドを追加できる」ってことを知ったのもこの3日間という感じ。
js_of_ocamlのレベルも似たり寄ったりかな。
動機
OCamlでコード書いててGUI付けたいと思いjs_of_ocamlでHTMLのGUI作ろうとしたけど、ViewとModelをうまく分離する方法がわからなかった。
そこで、F#+WPFみたくMVVMな感じで
を実現したいなーと思い調べたところVue.jsを知り、とりあえず作ってみました。
やったことはVue.jsの以下のサンプルコードをjs_of_ocamlで写経しました。
app.js(Vue.jsのサンプルコード)
var demo = new Vue({ el: '#demo', data: { branch: 'master' }, created: function () { this.$watch('branch', function () { this.fetchData() }) }, filters: { truncate: function (v) { var newline = v.indexOf('\n') return newline > 0 ? v.slice(0, newline) : v }, formatDate: function (v) { return v.replace(/T|Z/g, ' ') } }, methods: { fetchData: function () { var xhr = new XMLHttpRequest(), self = this xhr.open('GET', 'https://api.github.com/repos/yyx990803/vue/commits?per_page=3&sha=' + self.branch) xhr.onload = function () { self.commits = JSON.parse(xhr.responseText) } xhr.send() } } })
app_ml.ml(上記のjs_of_ocaml版)
open Js (* Vueオブジェクトのインスタンスパラメーターを定義 *) let param = let open Unsafe in (* Unsafeをよく使うので開いてしまう *) let created () = (* Javascriptのthisの有効範囲がよくわかってないのでその都度取り込む *) let this = Unsafe.variable "this" in meth_call this "$watch" [| inject (string "branch"); inject (fun () -> (* このthisの書き方でいいのかな *) meth_call this "fetchData" [||]) |] |> ignore in (* truncateとformatDateは実際にはModelの仕事なのでOCamlっぽく書いてみる *) let truncate v = try let str = to_string v in let newLine = String.index str '\n' in String.sub str 0 newLine |> string with _ -> v in let formatDate v = let str = to_string v in let rexp = Regexp.regexp "T|Z" in Regexp.global_replace rexp str " " |> string in let fetchData () = let this = Unsafe.variable "this" in let self = this in let branch_js = self##branch in Firebug.console##log(branch_js); let branch_ml = to_string (branch_js) in let url = "https://api.github.com/repos/yyx990803/vue/commits?per_page=3&sha=" ^ branch_ml in let (>>=) = Lwt.(>>=) in (* 記号を導入*) XmlHttpRequest.get url >>= fun r -> let msg = r.XmlHttpRequest.content in (* let json = Json.unsafe_input (string msg) in *) (* Firebug.console##log(json); *) (* こう書きたいんだけjs_of_ocamlのJsonオブジェクト内の文字列が mlstringになってて期待した通りに動かないのでJavascriptのJSONを輸入する *) let json = Unsafe.variable "JSON" in self##commits <- Unsafe.meth_call json "parse" [| inject (string msg) |]; Lwt.return msg in obj [| "el", inject (string "#demo"); "data", inject (obj [| "branch", inject (string "master") |]); "created", inject created; "filters", inject (obj [| "truncate", inject truncate; "formatDate", inject formatDate |]); "methods", inject (obj [| "fetchData", inject fetchData |]); |] (* JS Objectをコンストラクタの引数にとるVueオブジェクトを定義 *) let vue : 'a constr = Unsafe.variable "Vue" (* Vueオブジェクトのインスタンスを定義 *) let demo = jsnew vue(param)
感想
とりあえず、UnsafeいっぱいであんまりOCamlのお得感はないですね。
そして、なんとなくVue.jsはよくできているように思いました。こんな感じでJavascriptでもMVVMできるっていうのが新鮮。
あと、js_of_ocamlとVue.jsの日本語のブログ記事はほぼすべて目を通しました。超参考になりました。ありがとうございました。
たとえばjs_of_ocamlは
Vue.jsは
などが役に立ちました。
実行デモとソースは以下です。
http://toku.bitbucket.org/experiment/vuejs/index.html