Web Developer's Struggle Memories

日々の業務から思ったこと、学んだことを書き連ねていきます。

Riot.jsに触れてみた話 Riot.jsの文法というか書き方

※2017年10月19日 追記

本記事のriot.jsは、バージョンは2のものとなります。 現在の最新バージョンは3.7.3ですので、参考にならないこともありますので、ご注意ください。

はじめに

前回の続きで、今回はもっと具体的にRiot.jsの書き方に触れてみる。
※よくあるチュートリアルのようなものを書く予定なので、自分の備忘録的に書きます。 したがって、随時更新の形にします。

cognitomさんの記事にRiot.jsのソースコードをとても詳しく解説されているので、 細かい仕様まで確認したい方はご参照ください。

書き方

opts変数

前回の最期でもちょっとだけ書いたopts変数について。tagファイル内で使われる変数だが、本当に中身はこれだけなのか疑問に思ったので、コンソールにopts変数の中身を出力してみた。 マウントした値は以下。

<script>
    riot.mount('app',{
        title: 'Hello Riot.js!',
        items: [1, 2, 3],
        text: 'foo'}
    )
</script>

結果は以下。

f:id:kito0039:20150813165633j:plain

本当にマウントした値のみだったので初期化用かな。ちなみに、同じtagファイル内に記述しているのであれば、アクセスはもちろんthisを使う。
 

変数、メソッドの指定

Riot.jsでは、変数やメソッドを{}で指定する。この後よく出てくるので、そういうものと思っていただければ。 とりあえず変数の例を載せる。

<app>
    <h1>{ opts.title }</h1>
    <p>{ message }</p>
    <script>
        this.message = "hogehoge";
    </script>
</app>

 

イベントハンドラ

ボタンをクリックした時などのイベントハンドラの書き方について。 例は一番多そうなボタンクリックonclickの場合。

<app>
    <h1>{ opts.title }</h1>
    <button onClick={ check }>チェック</button>

    <script>
        check(e) {
            // チェックボタンが押された時の処理
        }
    </script>
</app>

もう一個、サブミットonSubmitの場合。

<app>
    <h1>{ opts.title }</h1>
    <form onSubmit={ doCheck } >
        <input type="text" value="">
        <input type="submit" value="チェック">
    </form>
    <script>
        this.text = "";
        doCheck(e) {
            // submit時の処理
        }
    </script>
</app>

イベントハンドラの指定には丸カッコ()はいらない。また、イベントハンドラが呼ばれた後は自動でthis.update()が呼ばれ、テンプレートが更新される。 他にはonKeyuponMouseoverなどいろいろ試したのだが、onLoadだと動作しなかった。おそらく不具合かな?

※追記
Riot.js公式ページのTag lifecycleListening to lifecycle eventsの項目を確認したところ、マウントされた後はupdateイベントが発火する仕様だそうな。なので、イベントハンドラが呼ばれたら自動的にupdateされるので、onLoadイベントは必要ない。またmount, updateイベントが発生した際に、this.on("mount", function(){})this.on("update", function(){})としてリスナーを登録できるとのこと。

css周り

  • styleタグについて
    htmlと同じように、tagファイル内でも<style>は使用できる(ちなみに複数指定可)。tagファイル内に書くと、Riot.jsが自動で<head>の最後に挿入してくれる。
    <script>内では波括弧{}は評価されないので仕様不可。
<app>
    <!-- layout -->
    <h1>{ opts.title }</h1>
    <button onClick={ check }>チェック</button>

    <!-- css -->
    <style>
        li {
            list-style-type: none;
        }
        .done-true {
            color: gray;
            text-decoration: line-through;
        }
    </style>
</app>
  • class属性について
    Riot.jsでは、その値が真になるプロパティ名は、クラス名のリストに追加される仕様となっている。例えば、
<p class={ foo: true, bar: 0, baz: new Date(), zorro: 'a value' }></p>

と書くと、<p class="foo bar zerro">となる。

繰り返し(ループ)

html側のループはこんな感じ。tasksという変数に配列が格納されていた場合の書き方。
body, doneというキーで値が取得できる。

<app>
  <!-- layout -->
  <ul>
    <li each={ tasks }>{ body }</li>
  </ul>

  <!-- javascript -->
  <script>
    this.tasks = [
      {"body":"do this 1", "done": false},
      {"body":"do this 2", "done": false},
      {"body":"do this 3", "done": true},
      {"body":"do this 4", "done": false},
    ];
    // 結果
    // do this 1
    // do this 2
    // do this 3
    // do this 4
  </script>
</app>

また以下のように、ループのインデックスを取得・表示することもできる。

<app>
  <!-- layout -->
  <ul>
    <li each={ task, key in tasks }> { key }: { task.body }</li>
  </ul>

  <!-- javascript -->
  <script>
    this.tasks = [
      {"body":"do this 1", "done": false},
      {"body":"do this 2", "done": false},
      {"body":"do this 3", "done": true},
      {"body":"do this 4", "done": false},
    };

    // 結果
    // 0: do this 1
    // 1: do this 2
    // 2: do this 3
    // 3: do this 4
  </script>
</app>

さらに連想配列の形式でのオブジェクト配列の場合。上記とほぼ記述の仕方は同じだが、微妙に違うのでこれは地雷かなと。

<app>
  <!-- layout -->
  <ul>
    <li each={ key, task in tasks }> { key }: { task.body }</li>
  </ul>

  <!-- javascript -->
  <script>
    this.tasks = {
      "a": {"body":"do this 1", "done": false},
      "b": {"body":"do this 2", "done": false},
      "c": {"body":"do this 3", "done": true},
      "d": {"body":"do this 4", "done": false},
    };

    // 結果
    // a: do this 1
    // b: do this 2
    // c: do this 3
    // d: do this 4
  </sciprt>
</app>

ループのソースを確認したところ、以下のように書いてあった。(ver2.4.1

function each(els, fn) {
  var len = els ? els.length : 0

  for (var i = 0, el; i < len; i++) {
    el = els[i]
    // return false -> current item was removed by fn during the loop
    if (el != null && fn(el, i) === false) i--
  }
  return els
}

つまりhtml側でノードについての処理はあるけど、JavaScript側で例えばAngular.jsならばangular.forEach()のようなメソッドはなさそう。

条件分岐

条件分岐でも、true or falseならば、htmlタグで記述できる。先ほどのonSubmitの例をちょっと変えてみる。

<app>
    <!-- layout -->
    <h1>{ opts.title }</h1>
    <form onSubmit={ doCheck } >
        <input type="text" onkeyup={ input }>
        <input disabled={ !text } type="submit" value="チェック">
    </form>

    <!-- javascript -->
    this.text = "";

    input(e) {
        this.text = e.target.value;
    }

    doCheck() {
        // submit時の処理
    }

</app>

サブミット用のボタンのdisabled属性はthis.textの値が設定されている(true)か否(false)で分岐するが、 特にJavaScriptで記述することはない。

次はif文の使い方。先ほどの例ではthis.textの値によって、チェックボタンが押せたり押せなくなったりするが、 以下はボタンそのものが表示されたり、表示されなくなったりする。

<app>
    <!-- layout -->
    <h1>{ opts.title }</h1>
    <form onSubmit={ doCheck } >
        <input type="text" onkeyup={ input }>
        <div if={ text }>
            <input type="submit" value="チェック">
        </div>
    </form>

    <!-- javascript -->
    this.text = "";

    input(e) {
        this.text = e.target.value;
    }

    doCheck() {
        // submit時の処理
    }
</app>

ルーティング

ある意味でこれが一番の目玉かも。最近はやりのSPAサイトを作るとしても、すごく簡単に書ける。

<app>
    <!-- layout -->
    <h1>{ opts.message }</h1>

    <ul>
        <li><a href="/">HOME</a></li>
        <li><a href="/page1">Page1</a></li>
        <li><a href="/page2">Page2</a></li>
        <li><a href="/page3">Page3</a></li>
    </ul>
    <route></route>

    <!-- javascript -->
    riot.route.start()
    riot.route.base('/')
  
    riot.route('/page*', function(tag) {
       riot.mount('route', 'page'+tag)
    })
  
    riot.route('', function() {
        riot.mount('app',{
            title: 'Routing demo',
        })
    })
</app>

<!-- other tags -->
<page1>
   <h1>Page1</h1>
</page1>

<page2>
   <h1>Page2</h1>
</page2>

<page3>
   <h1>Page3</h1>
</page3>

終わりに

まだまだ勉強中なのであくまで基本的なことしかやってない。 次は簡単だけどtodoリスト以外の何かを作ってみたい。やっぱり何かを実際に作った方がよっぽど力になる。(作るものは検討中)

では。