この記事はRiot.js Advent Calendar 2016の13日目の記事です。
前日の方と同じようなテーマになってしまって申し訳ないです!
普段はCakePHPでのサイト構築やNode.jsでWebAPIプログラムを書いたりしてます。
フロントではjQueryしか使わない、JavaScriptフロントエンドライブラリ経験1ヶ月程度のゆるふわな感想ですので、生暖かく見守っていただければ幸いです。
きっかけ
- Node.jsで動いてるWebAPI(戻りはJSON)をWebブラウザでモニタリングする機能をサクッと作れないかなー
- PHPならサクッと組めるけどNode.jsを運用しているサーバにPHPを新たに入れるのは嫌だなー
- jQueryでDOM操作して書くのも大変だよなー(実際やったけど可読性最悪でメンテがしんどかった)
- そうだ、JavaScriptのフロントエンドライブラリを使ってみよう!
jQueryはすごく便利だが…
jQueryを極めたかどうかは別として、jQueryである程度コードを書けるようになると、「jQueryと水さえあれば生きていける!!」状態になります(個人の感想です)。
先日、JavaScriptのアドベントカレンダーの方に投稿したネイティブJavaScriptとjQueryの比較をしてみて、改めてjQueryの便利さを再認識した次第です。
しかしながらjQueryオンリーでの極度なDOM操作はしんどいものです。書き始めた時はぷよぷよで連鎖を重ねる如く、メソッドチェインを繋げることに快感を覚えるんですが、ファイルを閉じたら最後、次の日見てみたらなんじゃこりゃー!!!ってなります(ました)。jQueryのメソッドチェインは「ダイヤキュート」ぐらいに留めておき、間違っても「ばよえ~ん」を詠唱しないように心がけるべきです。
と、どうでもいい前置きは置いておいて、前々から気になっていたJavaScriptフロントエンドライブラリを使ってみることにしました。
はじめはAngularJSを使っていた
JavaScriptのフロントエンドライブラリの二大巨頭といえば「AngularJS」と「React」のようです。ぐぐってもだいたいこの2つが上位に出てきます。一方はフルスタックのフレームワーク、もう一方はView特化型のUIライブラリ。どちらにしようか悩んだ挙句、とりあえずGoogleが作ってるし使ってみるか、ということでAngularJS(1系)を使ってみました。
なるほど、何から何まで揃っていてこりゃ便利だ。
しかしAngularJSはフルスタックのフレームワークなだけあり覚えることも多く、ツール的なものを作るには少々持て余す感じでした。
例えて言うならば、近所のコンビニにジュース買いに行くのに車で行く感じ?
正直そんな大掛かりなものは要らないですし、近所のコンビニにジュースを買いに行く程度なら徒歩かよくても自転車です。
そんなこんなで別のライブラリも試してみようということになり、色々調べているうちに以下の記事に辿り着きました。
フロント界隈で一番イケてるのは AngularJS でも React でもなく Riot.js だという話
そこで、Riot.jsに興味を持ち、公式サイトのよくある質問と答えに大変痺れるお言葉が…
IE8をなんでサポートしないの?
やっと、この低品質ブラウザを無視しても大丈夫になりました。
よくぞ言ってくれた!!この言葉を待っていた!!!!!
というわけで、Reactをすっ飛ばしてとりあえず使ってみることにしました!
Riot.jsのよいと思ったとこ
直観的に書ける
たった1ファイル(Riot.js+インブラウザコンパイラ)読み込ませるだけで、HTMLファイルが一般的なテンプレートエンジンのように早変わり。見た目上はほぼHTML+JavaScriptで、AnglureJSのような独特なディレクティブも少なく、直観的に書けるのは素晴らしいと思います。
条件分岐ならif
、繰り返しならeach
、イベントに至っては昔ながらのonXXXX
(onclickなど)そのまんまですね。
{}
で囲まれたのがテンプレート変数、この変数はまんまJavaScriptなので関数をかませたりももちろんできます。
<test></test><!-- ここに出力される -->
<!-- Riot.js+インブラウザコンパイラを読み込む -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/riot/2.6.7/riot+compiler.js"></script>
<!-- 実処理内容(外部ファイルにすることも可。ここではインラインで書く) -->
<script type="riot/tag">
<test>
<!-- テンプレートとなる部分 -->
<table>
<tr each={num in numbers}>
<td>{num}</td>
</tr>
</table>
<!-- /テンプレートとなる部分 -->
// 処理となる部分:START
this.numbers = [1,2,3];
// 処理となる部分:END
</test>
</script>
<script>
// testタグをマウントして実行
riot.mount('test');
</script>
コンポーネント指向
パーツごとに管理できるというのがいいですね。
上の例ですと、testタグがひとつのパーツであると考えられます。
しかもタグごとにHTML,JavaScript,CSSを独立したファイルとしてまとめておけるのでこれまた便利。
シンプル イズ ベスト
Riot.jsには必要最小限の機能しかありません。標準機能で出来ることは標準機能に任せるという思想みたいです。
必要最低限の機能しかないということは、Riot.jsの自体の学習コストが低い、標準機能で実装したコードは他に使いまわせる(仮にRiot.jsを使わなくなった場合のコストも低くなるハズ)などのメリットが考えられます。
たとえば、WebAPIのデータを取得するために必要なAjax通信ですが、Riot.jsにはその機能がありません。
え?じゃあjQueryのような外部ライブラリ入れるか、あの原始的なXMLHttpRequestで書かないとダメなの???
いいえ、違います。
まだ実験段階ではありますがFetch APIなんて便利な機能がいつの間にか標準APIとして提供されつつあるんですね。
→ お疲れさまXMLHttpRequest、こんにちはfetch
しかもこのAPIはPromiseを返してくれるのですごく扱いやすい。
※ Promiseって慣れるとホント便利です…手前味噌ですが、
→ 約束って面倒くさい。
jQueryも使えますよ
Riot.js FAQ
ただし、DOM操作はRiot.jsに任せて、イベントデリゲートやプラグインの活用のみに限定した方がよさそうです。
Riot.jsで惜しいなと思ったとこ
テキスト整形機能が欲しい
ある程度作りこむとなると、AngularJSのfilter機能的なものが欲しくなります。
サーバ側で取得してきたデータをそのまま無加工で表示できればいいんですが、実際はそういうわけにもいかないことが多いと思います。(特に日時データとか)
それに加え、JavaScriptにはマトモなテキスト整形機能がありません。
せめて日付フォーマットの整形機能があってもいい気がするんですが、それすらありません。
AngularJSだと以下のようなコードで日付フォーマット整形ができます。
{{current_date|date:'yyyy/M/d hh:mm'}}
Riot.jsでは自前で関数を書いて実現します。
※フォーマット出力すると大変なのでとりあえず固定フォーマットで
<test>
{date(current_date)}
date(date_string) {
let date = new Date(date_string);
let y = date.getFullYear();
let m = ("0"+(date.getMonth()+1)).slice(-2);
let d = ("0"+date.getDate()).slice(-2);
let h = ("0"+date.getHours()).slice(-2);
let i = ("0"+date.getMinutes()).slice(-2);
return `${y}/${m}/${d} ${h}:${i}`;
}
</test>
Viewに特化しているからこそ、こういう機能は欲しいと思いました。
資料が圧倒的に少ない
日本語はともかく、英語の資料もあまりないような…?
自分の検索の仕方が悪いだけ?
実際書いてみた
JSONを取得して表示するだけのコードです。
<!DOCTYPE html>
<html>
<head>
<title>json monitor</title>
<meta charset="UTF-8">
<script src="//cdnjs.cloudflare.com/ajax/libs/riot/2.6.7/riot+compiler.js"></script>
</head>
<body>
<json-monitor></json-monitor>
<script type="riot/tag">
<json-monitor>
<span if={error != ''}>{error}</span>
<table if={error == ''}>
<tr>
<th>ID</th>
<th>名前</th>
<th>更新時間</th>
</tr>
<tr each={data in datas}>
<td>{data.id}</td>
<td>{data.name}</td>
<td>{conv_datetime(data.modified)}</td>
</tr>
</table>
this.error = '';
// APIからJSONデータを取得
fetch('/api/hoge.json')
.then((data) => {
return data.json();
})
.then((datas) => {
this.update({datas});
})
.catch((error) => {
this.update({error});
});
// 日付変換メソッド
conv_datetime(date_string) {
let date = new Date(date_string);
let y = date.getFullYear();
let m = ("0"+(date.getMonth()+1)).slice(-2);
let d = ("0"+date.getDate()).slice(-2);
let h = ("0"+date.getHours()).slice(-2);
let i = ("0"+date.getMinutes()).slice(-2);
return `${y}/${m}/${d} ${h}:${i}`;
}
</json-monitor>
</script>
<script>
riot.mount('json-monitor');
</script>
</body>
</html>
[{"id":1,"name":"hoge","modified":"2016-12-12 10:00:00"},{"id":2,"name":"fuga","modified":"2016-12-13 10:00:00"}]
これだけのコード量で完結するんだからすごい。
※実際はリクエストURLにパラメータを与えたり、ちゃんと例外処理書いたり、レイアウト整えるためにCSSとか書かないとダメですが。
JSONは(当然ですが)JavaScriptと相性いいのでサクサク書けるのはいいですね。
ちなみに自環境で動けばいいツールでしたので、レガシーなWebブラウザじゃ動かないと思います!
さいごに
今回触れたのはRiot.jsの機能のほんの一部分です。
ルーティング処理や、イベント周りを全然追えて追えていない状況ですので、薄っぺらい内容になってしまいましたが、シンプルさが伝わってもらえれば幸いです。
ちょうど空きがありましたので、明日も引き続き書かせていただきます!