Help us understand the problem. What is going on with this article?

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

More than 3 years have passed since last update.

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

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

Riot 2.0 | A React- like UI library.png

Riotって何?

Riotは、公式ページに

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は既存サイトにエンベッドして使う、フォーラムツールを作っているチームです。(Disqusみたいなやつ)

たとえばこんなコード

todoという独自タグ(=コンポーネント)を実装するには、こんなコードになります。

<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に似た文法です。

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

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

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

コードブロック

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

<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から

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

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

<!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コマンド用意されていて、事前にコンパイルしておくことも可能です。すでに、

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

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

Riot todo.png

簡単なデモが公式サイトに載っていますが、理解を深めるために、これを少し変更してみたいと思います。タスクリスト部分を別のコンポーネントにして、<li>だった部分を<todo-item>という新しい独自タグに置き換えてみます。

<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 <クラス名>のような形で指定すると、コンポーネントの要素だけを指定できて便利です。

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

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

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

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

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

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away