JavaScript
riot
riot.js

Riot.js ことはじめ

More than 1 year has passed since last update.

端的に結論を言うと、もうこれで良いじゃないかな、です。

背景

ちょっと前にこのへんとかを中心にWebのフロントエンドを作るのにフレームワークはいるかいらないかみたいな議論(適当)がありましたが、業務で静的データを使ったWebページ(サーバサイドの状態がフロントエンド側から変化しないページ)を作ることが多い自分としてはこう思いました。

  • 確かにjQueryで直接DOMをいじるのは、いじる要素が片手で数えられるうちはいいが、それを超えるとうまく管理できなくなるからあんまり上手い方法でないのは同意できる
  • かといってReactJSは確かに便利そうではあるが、正直ぱっと見ではよくわからないので、使うなら腰を入れて勉強する必要がありそう=ミニマムスタートしにくそう

ということでうまく折衷案的なものはないかと思っていたら riot.js というのを見つけたので試してみました。

Riot.jsの超入門

Riot.jsでできる基本的な機能をまとめてみました。ちなみにこの記事のサンプルはほぼとりあえず試してみたいって方のための Riot.js 入門を参考に写経させてもらったものです。

基本形

最小構成のサンプルです。

<!doctype html>
<html>
  <head>
    <meta charset="utf-8" />
    <script src='https://cdnjs.cloudflare.com/ajax/libs/riot/2.3.18/riot+compiler.js'></script>
    <title>RiotJS Test</title>
  </head>
  <body>
    <!-- タグを展開する場所 -->
    <item name='Annoy-o-Tron' quote='Hello, Hello, Hello' ></item>

    <!-- タグの定義 -->
    <script type="riot/tag">
      <item>
        <h1>{ name }</h1>
        <p>{ quote }</p>

        this.name   = opts.name;
        this.quote = opts.quote;
      </item>
    </script>

    <script>
      // マウント
      riot.mount('*');
    </script>
  </body>
</html>

これが以下のように展開されます。

Screen Shot 2016-06-19 at 1.23.53 PM.png

複数要素を展開する場合は<item>を足せばOKです。(bodyの中だけ記述)

  <body>
    <item name='Annoy-o-Tron' quote='Hello, Hello, Hello' ></item>
    <item name='Northshire Cleric' quote='Is someone injured?' ></item>

    <script type="riot/tag">
      <item>
        <h1>{ name }</h1>
        <p>{ quote }</p>

          this.name  = opts.name;
          this.quote = opts.quote;
      </item>
    </script>

    <script>
      riot.mount('*');
    </script>
  </body>

この例の <item>タグをカスタムタグと呼びます。Riot.jsのユーザインターフェースの構成要素になります。

また、複数の要素を展開したいときは、script内でループを記述することもできます。

  <body>
    <item></item>
    <script type="riot/tag">
      <item>
        <!-- this.item_list  {item_list} として each属性で指定 -->
        <div each='{item_list}'>
          <h1>{ name }</h1>
          <p>{ quote }</p>
        </div>

        // JS内で要素のデータを定義する
        this.item_list = [
          {name: 'Annoy-o-Tron', quote: 'Hello, Hello, Hello'},
          {name: 'Northshire Cleric', quote: 'Is someone injured?'},
        ]
      </item>
    </script>

    <script>
      riot.mount('*');
    </script>
  </body>

同じように展開されます。

Screen Shot 2016-06-19 at 2.05.51 PM.png

イベント処理

カスタムタグ内のイベントハンドラもカスタムタグの定義内に書くことができます。以下、さきほどのサンプルにbuttonをつけてみた例です。

  <body>
    <item></item>
    <script type="riot/tag">
      <item>
        <div each='{item_list}'>
          <h1>{ name }</h1>
          <p>{ quote }</p>
          <button onclick='{clicked}'>play</button>
        </div>

        // JS内で要素のデータを定義する
        this.item_list = [
          {name: 'Annoy-o-Tron', quote: 'Hello, Hello, Hello'},
          {name: 'Northshire Cleric', quote: 'Is someone injured?'},
        ]

        // イベントハンドラを定義
        this.clicked = function(e) {
          console.log(e);
          console.log(`played ${this.name}`);
        }
      </item>
    </script>

    <script>
      riot.mount('*');
    </script>
  </body>

this.clickedに格納したイベントハンドラをbuttonのクリック時に呼び出しています。thisはイベント発火時の要素が入るので、this.nameで名前を取得することができます。以下、下のボタンをクリックした時の出力例です。

Screen Shot 2016-06-19 at 2.11.20 PM.png

個別CSSの記述

カスタム内部だけに有効なCSSを定義することができます。これによっていままで分断されていたHTML、CSS、JavaScriptを小さいスコープで一括管理できるようになります。個人的にはこれは非常に強力だと思います。

カスタムタグ内にstyleタグを使って定義しますが、タグ記述の際に<style scoped>と記述すること、対象となるCSSに :scope を記述します。

  <body>
    <item></item>
    <p>ここのpタグは影響をうけない</p>

    <script type="riot/tag">
      <item>
        <div each='{item_list}'>
          <h1>{ name }</h1>
          <p>{ quote }</p>
        </div>

        <style scoped>
          :scope p {
            color: red;
          }
        </style>

        this.item_list = [
          {name: 'Annoy-o-Tron', quote: 'Hello, Hello, Hello'},
        ];
      </item>
    </script>

    <script>
      riot.mount('*');
    </script>
  </body>

表示の更新

Riot.jsは再レンダリングするタイミングが幾つかあります。以下に例を示します。

  <body>
    <item></item>

    <script type="riot/tag">
      <item>
          <button onclick='{add}'>add</button>
        <div each='{item_list}'>
          <h2>{ name }</h2>
        </div>

        this.item_list = [];

        this.add = function(e) {
          this.item_list.push({ name: '要素' + this.item_list.length });
        }

        const self = this;
        setInterval(function() {
          self.item_list.push({name: 'tick'});
          self.update();
        }, 1000);
      </item>
    </script>

    <script>
      riot.mount('*');
    </script>
  </body>

1つがイベントハンドラが呼び出された時なので、this.add が呼び出された時は特に指定をしなくても this.item_list に追加したデータを表示してくれます。

一方、setIntervalで一秒ごとにデータを追加していますが、これはイベントハンドラの呼び出しではないので、明示的に対象要素の this.update() を呼び出す必要があります。上記サンプル中では、setInterval内ではthisが対象要素ではなくなってしまうため、selfに格納させてそこから参照しています。

数秒ぐらいにaddボタンを押すと以下のようになります。

Screen Shot 2016-06-19 at 2.45.49 PM.png

Riot.js のよさ・注意

正直なところ自分はReactやAngularなど、他のフレームワークであまり開発した経験がないので Riot.js について直感的に思ったところを述べます。

シンプル

直感的に思ったのが使い方がシンプルだということです。HTML, CSS, JavaScriptを知っている人がドキュメントを見ずにコードだけを見て「あ、こういう感じに動くんだろうな」という想像がつきます。他フレームワークでも同じことができるとは思いますし、シンプルということは複雑な処理がしにくいということかもしれませんが、同じ機能をつかうだけなら学習コスト、保守などの観点からシンプルさは正義です。

分割統治

正直自分としてはこれが一番よさを感じました。ReactでもpracticalにCSSを内包させる書き方もあるみたいですが、Riot.jsだと標準的にHTML + JS + CSSをコンポーネント化できて、しかもネストもできるようです。

ある程度のコンテンツ量になってくると、どうしてもCSSやJSがとっちらかってきてしまい、統制するためにはある程度の頑張りが必要になります。エンジニアたるものちゃんと統制しろという話はもちろんありますが、頑張らなくていいことは頑張りたくないのが人情です。スコープを分割してシンプルにまとめることで保守がしやすくなり、可搬性も高くなるというメリットは大きいと思います。

アップデートの早さ

一方注意したい点というべきか、本来は良い点と捉えるべきなのかもしれませんが、まだマチュアではない部分が多いようです。ちゃんと追えていませんが、記述方法などもちょいちょい変更が入っているようですし、(自分が悪いだけかもしれませんが)公式ドキュメントに書いてあるやりかただと動かない…みたいなこともちょいちょい。なので、使う場合は特にバージョンとの整合性などに気をつけたほうがいいかもです。

まとめ

ということで、個人的にはかなり気に入りました。

次に何かフロントエンドを作成する時にはぜひ採用してみたいと考えています。

参考文献