jQueryではもうアカンと思った時に挙った乗り換え候補はRiot.jsとMithril.js。
どっちもナウいんで一応両方試してみたんだけども、Riot.jsに落ち着いた感じであります。
なんでRiot.jsになったかというと、完全に個人的な好みの問題でして、
すごい大雑把にいうとRiot.jsはHTMLにJSを書くという方針に対してMithril.jsはJSにHTMLを書くという親のReact.jsの流れを汲む文化を継承してるんですが、試しで作ってる時に「HTMLにJS書く方が違和感ない」と思ったからなのでした。
あとはRiot.jsが企業に対してMithril.jsは個人であるといった、開発母体の差もちょっと気になる点でした。
機能面はどちらもミニマムでシンプルでAPI数少なくてとっつきやすいのでそんなに変わらないが、学習コストは独自ルールが多いRiot.jsの方が数段高いと思う。
でこうして紹介するにしてもTodoじゃ面白みがないんで、
実際使えそうなものをサンプルを作ろうとして思いついたのが、
Googleのスプレッドシートで更新情報表示するというものです。
昔普通に商用サイト作ってた頃「ホームページに更新情報表示したい(自分でお知らせ書きたい)」ってな要望が多かったので、今ならこういう手も使えるかなと思いまして。
スプレッドシートをデータベースにするというサンプル自体は4年前にやってたので
http://jsdo.it/Tenderfeel/ahd8
これのガワをbackbone.jsからriot.jsに変更しただけのものですが、
Google visualization API + Riot.js sample
ソースを比較するとえらいこざっぱりしているのがお分かり頂けるかと思います。
backbone.jsと比べても可読性めっちゃ上がってると思う〜。
続きはplnkerにつくったサンプルの解説です。
全体像
ファイルは2つ。
- index.html ー このHTMLを表示する
- news.tag ー 更新情報の本体部分
index.htmlにはbodyにnews
というタグがありますが、これがnews.tagの表示先となります。
当然の事ながらnews.tagだけ表示してもなにもおきません。
拡張子.tagは、カスタムタグのソースファイルである目印のようなものです。
コンパイラはtype属性で認識してるのでファイルの拡張子は.jsや.htmlでも動きます。
マウント
riot.js本体はちょっと試すならCDNから読み込むのが手っ取り早いかなと思います。
コンパイルだけ先に済ましてriot.jsだけ読み込む方がより快適になります。
<script src="https://cdn.rawgit.com/riot/riot/master/riot+compiler.min.js"></script>
タグのファイルを読み込む時の作法で必須なのはtype属性で”riot/tag”を指定する事。
スクリプトタグは↑の本体より上に書くのが良いです。
<script src="news.tag" type="riot/tag"></script>
コンポーネントにするタグ名はなんでもいいです。
サンプルでは更新情報らしくnewsとしています。
<news></news>
※ちなみにHTML5準拠のタグ(headerとか)でもそのままコンポーネント化できてしまう。
最期にriot.mountでマウントするタグを指定すればおk。
タグの名前をちゃんと明記するのが面倒なほど数があるなら、アスタリスクで大雑把に指定することもできる。
riot.mount('news', {key: '1lR2wGFPfUhagtnviR7YVnjNvnCNe7nkziUFvAMXMCZw'});
第2引数でオプションを渡せますが、これはタグで次の様に書いてもおk。
<news key="1lR2wGFPfUhagtnviR7YVnjNvnCNe7nkziUFvAMXMCZw"></news>
渡されたオプションはタグ側でopts
プロパティに含まれます。
タグ側の挙動
riot.jsによってマウントされたタグは、HTMLとScript部分が実行される。
scriptタグのthis参照はタグ自身となっていて、windowから切り離された独自の世界です。
そのため変数や関数でthis
を付けた場合は親や子タグからparentやtagsプロパティ経由でアクセス可能になるが、
var
で宣言した場合はローカルなものとなりアクセス出来なくなります。
var self = this; //news.tag内でのみ利用出来る this.key = opts.key || ''; //オプションの取得 this.query = new google.visualization.Query('http://spreadsheets.google.com/tq?key=' + this.key); this.items = []; //他のタグからも利用できる
関数はfunctionを省略することも出来るが、この書き方をした関数はthisを付けた関数と同じ扱いになるもののHTMLからしか使用することができない。
itemsFilter(item) { if (item.show) { return item; } }
※この関数内のthis参照もタグ自身になってたりする
visualization.Queryの処理部分はbackbone.jsと同じで、整形したオブジェクトのpush先変数がthis.itemになったくらいです。
this.query.send(function(response) { var data = response.getDataTable(); for(var row = 0; row < data.getNumberOfRows(); row++) { var line = []; var obj = {}; obj['id'] = row; for(var col = 0; col < data.getNumberOfColumns(); col++) { var label = data.getColumnLabel(col); obj[label] = data.getValue(row, col); } self.items.push(obj); } self.update(); });
最期のself.update()
はriot.jsのメソッドです。
riot.jsはタグがマウントされたときやイベントハンドラが実行されたとき等は自動的にupdateしてくれますが、
こういうAjaxでなんかした時は手動でupdateを実行しないと結果が反映されません。
一方HTML側はというと、これそのものがテンプレートの趣があるので
見れば何をしてるのか大体察しがつくと思います:
<table if="{ items.length }"> <tr each="{ items.filter(itemsFilter) }"> <th scope="row"> <p class="date">{ formatDate(date) }</p> <p class="title">{ title }</p> </th> <td> { message } </td> </tr> </table>
何か条件によって表示を変えるというあるあるな処理について、riot.jsはif、show、hideが用意されてます。
ifは要素のaddとremoveで、show/hideはCSSのdisplay操作です。
ifの場合には、条件がtrueなら表示、falseなら非表示なので、
これはitems配列のlengthで判別して0ならtableを表示しないという指示です。
<table if="{ items.length }">
eachは配列やオブジェクトを1つずつループ処理させる指示です。
<tr each="{ items.filter(itemsFilter) }">
ここではfilterでitemsFilter関数を指定して、showが1(true)に指定してあるものだけ返すようにしています。
itemsFilter(item) { if (item.show) { return item; } }
ループ内ではプロパティとかそのまま書くだけ。
ここの書き方はeachで処理するモノによって変わるので詳しくは公式ドキュメント見て頂くとして、
<th scope="row"> <p class="date">{ formatDate(date) }</p> <p class="title">{ title }</p> </th> <td> { message } </td>
括弧の中はJSがほとんどそのまま使えるくらいの自由さがあるので、
ちょっと関数で処理したいなーと思ったらタグでその通り書くだけで済む。
{ formatDate(date) }
formatDate(date) { var d = new Date(date); return d.getFullYear() + '/' + (d.getMonth()+1) + '/' + d.getDate(); }
この関数他でも使いたいなあと思ったらmixin機能を使えば共有ができるよ:
riot.mixin('Utility', { formatDate: function(date) { var d = new Date(date); return d.getFullYear() + '/' + (d.getMonth()+1) + '/' + d.getDate(); } }); //使うとき this.mixin('Utility');
実際使ってみると懸念点もあるんですけども、
jQueryではもうアカンと思ったのが、ユーザーの操作が発生する箇所が増えて増えてonまみれになって
もうこれ1年分くらいon書いたんじゃねーかな…と思ったりしたからだったので
そういうユーザーの操作によって何かするモノをお手軽に作るには向いてるライブラリだと思います。
DOMを直接操作したりってことは苦手なんで、jQueryにとって変われるかというとそうでもなかったりするけど。