RiotはReactライクなマイクロMVPライブラリです。タスクランナーを噛ませたいので[こちらのサンプル](Adding compiled Riot tags to your Gulp + Browserify build)を動くようにしたり、いろいろいじってみた。

時計を表示するclockタグを表示します。インスタンスごとに長針・短針・秒針の表示を変えています。マウスホバーでポーズします。

via Adding compiled Riot tags to your Gulp + Browserify build

Riotタグを書く

Riotの特徴はなんといってもHTML+CSS+JavaScriptの混ぜ書き。一見HTMLに見えますが、JavaScriptとしてトランスパイルされます。

<!-- clock.tag -->
<clock>
  <svg>
    <line></line>
    <circle></circle>
    ...
  </svg>

  <style scoped>
    :scope {
      ...
    }
    svg {
      ...
    }
    ...
  </style>

  <script>
    // ↑このscriptタグは省略できる
    // ↓メソッド構文
    test() {
      ...
    }
    // function文も共存する
    function hogehoge() {
      ...
    }
  </script>
</clock>

シンタックスはとりあえず、スクリプト部分にscriptタグを入れてHTMLのシンタックスにしてみた。

scoped スタイル

scopedスタイルはトランスパイルされるとそのタグの子セレクタとなります。

clock svg {
  ...
}

Firefoxだけはscoped属性を実装しているようです。

メソッド構文

奇妙に見えますがRiotタグの中では

<clock>
  test() {
    ...
  }
</clock>

という書き方ができます。これはES6のクラス構文のような感じです。

class Clock {
  test() {
    ...
  }
}

同じタグの中にfunction文による定義が混じっていると、一瞬あれっ、てなります。

トランスパイル後は

this.test = function() {
  ...
}.bind(this)

となります。この時のthisはRiotタグのインスタンスになります。

メインで呼び出す

RiotとRiotタグをrequireしてmountします。

// main.js

var riot = require('riot');

require('./tags/clock.tag');

riot.mount('clock');

GulpでRiotをトランスパイル

Riotタグのトランスパイルはriotifyが用意されています。あとはbaberifyと同様ですね。

//gulpfile.js

var gulp = require('gulp');
var browserify = require('browserify');
var riotify = require('riotify');
var source = require('vinyl-source-stream');

...

gulp.task('concat', function () {
  return browserify({
    debug: true,
    entries: ['path/to/main']
  }).transform([riotify])
    .bundle()
    .pipe(source('main.bundle.js'))
    .pipe(gulp.dest('path/to/dest/'));
});

HTMLに組み込む

定義したタグとトランスパイルしたバンドルをHTMLに追加します。

<!-- index.html -->
<!DOCTYPE html>
<html>
  <head>
    <title>Riot Clock</title>
  </head>
  <body>
    <clock></clock>
    <script src="main.bundle.js"></script>
  </body>
</html>

ループ&子タグ

設定を配列で用意して子タグにオプションを渡す構造にしてみた。まず、親タグを書く。

<!-- clocks.tag -->
<clocks>
  <clock
    each={ clocks // means this.clocks }
    some-opts={ someOpts // means this.clocks[*].someOpts }
    another-opts={ anotherOpts // means this.clocks[*].anotherOpts }
    ...
  ></clock>
  <script>
    this.clocks = [{
      // this child has no options
   }, {
      // this child has options
      someOpts: true
    }, {
      // this child has another options
      anotherOpts: true
    },
    // ...
    }];
  </script>
</clocks>

他のデータバインドやテンプレート言語の経験から

<!-- Didn't work -->
<clocks each={ clocks }>
  <clock some-opts={ someOpts }></clock>
</clocks>

こう書きたくなりますが、違うみたいです。一方で、設定オブジェクトのプロパティをそのまま受け継いでおり、楽チンです。

メインでは両方のタグをマウントすればよし。

var riot = require('riot');

require('./tags/clocks.tag');
require('./tags/clock.tag');

riot.mount('*');

HTMLには親タグのみ記述

<clocks></clocks>
<script src="main.bundle.js"></script>

その他

モジュール化

タグ内部で普通にrequireを使えるので、今までBrowserify等使っていれば、同じ感覚でコーディングできます。

<!-- clock.tag -->
<clock>
  ...
  <script>
    var $ = require('jquery');
    ...
  </script>
</clock>

いろいろ外部化したほうが見やすいような気がします。Min-inのinitメソッドは自動で実行されます。

// mixin/clock.js

var ClockMixin = {
  init: function() {
    ...
  },
  test: function() {
    ...
  }
}
module.exports = ClockMixin;
<!-- clock.tag -->
<clock>
  ...
  <script>
    var ClockMixin = require('path/to/mixin');
    this.mixin(ClockMixin);
  </script>
</clock>

ファイルが別になってしまうと、コンポーネントっぽさが失われてしまうというのはありますね。こうとか?

<!-- clock.tag -->
<clock>
  ...
  <script>
    ...
    this.mixin({
      init: function() {
        ...
      },
      test: function() {
        ...
      }
    });
  </script>
</clock>

Mix-inはRiot的には、このように書くようです。これはまたシンタックスががが…

<!-- clock.tag -->
var ClockMixin = {
  init: function() {
    ...
  },
  test: function() {
    ...
  }
}
<clock>
  ...
  <script>
    ...
    this.mixin(ClockMixin);
  </script>
</clock>

使い方は簡単ですが、シンタックスとか記法に納得するのが大変かも。でも個人的には使ってみたいです。

https://github.com/110chang/riot-test