Riot.js 2.0 を触ってみた — まだReactで消耗しているの?


楽すぎてどうしよう。が最初の感触。まだ3時間しか触ってないけど、もうこれでいいや感が半端ない、深夜2時です。

Angularなのか、Reactなのか、2015年が明けても毎週のように新しいJSフレームワークが出る中で、もう正直どうでもよくなってませんか? でも、これは触って楽しいはず。

![Riot 2.0 | A React- like UI library.png](https://qiita-image-store.s3.amazonaws.com/0/1858/ec25035e-6d65-be24-75a7-cab8a80bb3fd.png "Riot 2.0 | A React- like UI library.png")

## Riotって何?

[Riot](https://muut.com/riotjs/)は、公式ページに

> A REACT- LIKE, 2.5KB USER INTERFACE LIBRARY

とあるように、Reactを意識して作られた超軽量のUIライブラリで、ビュー部分(コンポーネント)に特化しているのが特長です。Vue.jsとかとも同類です。Riot 1.0も「超軽量」という点で、一時注目を集めました。

そのRiotが、2.0で趣向を変えてJSX的なプリコンパイルの仕組みを取り入れて、ReactとPolymerのいいとこ取りのような感じになっています。ただし、次のような大きな違いがあります。

- React: JavaScript(JSX)の中に、HTMLを書く
- Riot: HTMLの中に、JavaScriptやCSSを書く

スクリプトサイズは24分の1。Reactが優に100KBを越えるのに対して、Riotはたった5.7KB。gzip圧縮かければ2.5KB。ちなみに、Riotの開発元の[MUUT](https://muut.com/)は既存サイトにエンベッドして使う、フォーラムツールを作っているチームです。(Disqusみたいなやつ) 


## たとえばこんなコード

`todo`という独自タグ(=コンポーネント)を実装するには、[こんなコード](https://muut.com/riotjs/)になります。

```html
<todo>
  <h3>{ opts.heading }</h3>
  <ul>
    <li each={ items }>{ title }</li>
  </ul>
  <form onsubmit={ add }>
    <input>
    <button>Add #{ items.length + 1 }</button>
  </form>

  this.items = []
  add(e) {
    var input = e.target[0]
    this.items.push({ title: input.value })
    input.value = ''
  }
</todo>
```

Reactだと、`this.state.something`とか、`this.props.something`が氾濫するところ、大分すっきりしてますね。HTMLの内側に、JavaScriptというのも納得感があります。

### 変数 opts

タグの属性として指定されたものは、コンポーネント内から`opts`変数を通じてアクセスできます。Reactの`this.props`の代わりに`opts`を使う感じ。`<todo heading="Hello Riot!"></todo>`だったら、`opts.heading`に"Hello Riot!"が入ってます。

### 繰り返し

Angularの`ng-repeat`にあたるのが、`each`属性です。テンプレート部分は、[mustache](https://mustache.github.io/)に似た文法です。

```javascript
items = [
  { title: '洗剤を買う' }
  { title: '牛乳を買う' }
]
```

のようなコレクションになっている場合、次のコードでOK。すっきり。

```html
<ul>
  <li each={ items }>{ title }</li>
</ul>
```

### コードブロック

Reactの逆で、HTMLの中に直接JavaScript(ES6)で書くことができます。ただ、この書き方だとエディタが対応していない場合もあるので、先ほどの例は`<script>`タグで明示して書くこともできます。

```html
<todo>
  <h3>{ opts.title }</h3>
  <ul>
    <li each={ items }>{ title }</li>
  </ul>
  <form onsubmit={ add }>
    <input>
    <button>Add #{ items.length + 1 }</button>
  </form>
  <script>
    this.items = []
    add(e) {
      var input = e.target[0]
      this.items.push({ title: input.value })
      input.value = ''
    }
  </script>
</todo>
```

`<script>`のtype属性には、現在のところ`coffeescript` `typescript` `es6` `none`が指定できるようです。

## HTMLへの組み込み

先ほどのコンポーネントは、拡張子`.tag`をつけて保存します(ここでは`todo.tag`)。その上で、メインのHTMLから

```html
<script src="todo.tag" type="riot/tag"></script>
```

として読み込みます。全体としては、次のようになります。`riot.mount`でコンポーネントを利用できます。

```html
<!doctype html>
<html>
  <head>
    <title>Riot todo</title>
    <link rel="stylesheet" href="todo.css">
  </head>
  <body>
    <todo></todo>
    <script src="todo.tag" type="riot/tag"></script>
    <script src="https://cdn.jsdelivr.net/g/riot@2.0(riot.min.js+compiler.min.js)"></script>
    <script>
      riot.mount('todo', {
        heading: 'Hello Riot!'
      })
    </script>
  </body>
</html>
```

上記は、ブラウザ上でトランスコンパイルする場合ですが、npmでインストール可能な`riot`コマンド用意されていて、事前にコンパイルしておくことも可能です。すでに、

- [gulp-riot](https://github.com/e-jigsaw/gulp-riot) - gulpプラグイン
- [grunt-riot](https://github.com/ariesjia/grunt-riot) - Gruntプラグイン
- [riotify](https://github.com/jhthorsen/riotify) - Browserifyのトランスフォーム

が用意されているので、既存の環境に組み込むのも簡単そうですね。


## コンポーネントをネストする

![Riot todo.png](https://qiita-image-store.s3.amazonaws.com/0/1858/b56e0bb2-14fa-9b2e-443a-cea75ea25390.png "Riot todo.png")

[簡単なデモ](http://muut.github.io/riotjs/demo/)が公式サイトに載っていますが、理解を深めるために、[これ](https://github.com/muut/riotjs/blob/gh-pages/demo/todo.tag)を少し変更してみたいと思います。タスクリスト部分を別のコンポーネントにして、`<li>`だった部分を`<todo-item>`という新しい独自タグに置き換えてみます。


```html
<todo>
  <h3>{ opts.title }</h3>
  <ul>
    <todo-item each={ items.filter(filter) } title={ title } done={ done } />
  </ul>
  <form onsubmit={ add }>
    <input onkeyup={ edit } value={ text }>
    <button disabled={ !text }>Add #{ items.filter(filter).length + 1 }</button>
  </form>
  <script>
    this.text = ''
    this.items = opts.items

    edit(e) {
      this.text = e.target.value
    }
    add(e) {
      if (this.text) {
        this.items.push({ title: this.text, done: false })
        this.text = ''
      }
    }
    filter(item) {
      return !item.done
    }
  </script>
</todo>

```

`<todo-item>`の実装はこちら。`<todo>`コンポーネント内で、属性として渡した値には`opts`変数からアクセスできます。Reactだとしばしば「CSSどうするの?」は問題になりますが、Riotだと、シンプルに`<style>`内に書けます。その際、 `todo-item <クラス名>`のような形で指定すると、コンポーネントの要素だけを指定できて便利です。

```html
<todo-item>
  <li>
    <label class={ completed: done }>
      <input type="checkbox" checked={ done } onclick={ toggle }>{ title }
    </label>
  </li>
  <style>
    todo-item .completed {
      text-decoration: line-through;
      color: #ccc;
    }
  </style>
  <script>
    this.done = opts.done
    this.title = opts.title
    
    toggle(e) {
      this.done = !this.done
      return true
    }
  </script>
</todo-item>
```

## まとめ

ここまで、[Riot](https://muut.com/riotjs/)のエッセンスが掴めそうなところをかいつまんで紹介してみました。でも実は、ほとんどドキュメントを読まずになんとかなってしまったことに、私自身がびっくりしています。AngularにしろReactにしろ、新しい概念を覚えてからじゃないととっつきにくいところがありますが、Riotにそういった部分はありません。(ドキュメント自体も大した量じゃないので、全部目を通しても30分もかからないくらい)

ここのところ、立て続けに新興フレームワークが世間を賑わしていますが、正直一番わくわくしました。

- [Ampersand.js](https://ampersandjs.com/)
- [mercury](https://github.com/Raynos/mercury)
- [Aurelia](http://aurelia.io/)

ほか、直近だけでもいろいろありました...。(全部は見きれてませんが)

現状の、Riotは2.0といっても、相当仕様を変えて来た所為もあってか、まだ結構しょうもないバグが含まれていたりします。さっき気づいたのだと、タグを閉じるときにスペースを空けないとバグるとか...。リリースから10日あまりで、2.0.7まで刻んできているので、その辺は推して知るべしというか。

とはいえ、サーバサイドレンダリングについても今後実装予定とのことですし、いろいろ楽しみです。引き続き、注目していきたいと思います。では。