Riot.js ドキュメント日本語版

  • 194
    Like
  • 2
    Comment
More than 1 year has passed since last update.

このドキュメントは、Riotの公式ドキュメントのWhy Riot?Riot developer guideの全訳です。修正あれば、こちらにプルリクエストください

追記・公式に取り込みました!
記事はこのまま置いておきますが、最新版は公式サイトを参照してください。

なぜ Riot?

答え1: カスタムタグ

RiotはIE8以降の全てのブラウザで、カスタムタグを実現します。

<todo>

  <!-- layout -->
  <h3>{ opts.title }</h3>

  <ul>
    <li each={ item, i in items }>{ item }</li>
  </ul>

  <form onsubmit={ add }>
    <input>
    <button>Add #{ items.length + 1 }</button>
  </form>

  <!-- logic -->
  <script>
    this.items = []

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

<todo>

カスタムタグは、関連するHTMLとJavaScriptをくっつけて再利用可能なコンポーネントとしてまとめます。React + Polymerに、"楽しい"文法と小さな学習曲線が一緒になったものをイメージしてください。

ヒューマンリーダブル

カスタムタグはHTMLで複雑なビューの構築を可能にします。あなたのアプリケーションはこんな感じになるでしょう:

<body>

  <h1>Acme community</h1>

  <forum-header/>

  <forum-content>
    <forum-threads/>
    <forum-sidebar/>
  </forum-content>

  <forum-footer/>

  <script>riot.mount('*', { api: forum_api })</script>
</body>

HTMLの文法はWebの デファクト 言語であり、ユーザインターフェースを構築するためにデザインされています。文法はシンプルで明確、入れ子構造が備わっていて、属性はカスタムタグにオプションを提供するための簡潔な方法です。

メモ タグファイルは、ブラウザで実行する前に、JavaScriptに 変換されます

仮想DOM

  • 最小のDOMの更新とリフロー
  • データは一方通行: 更新とアンマウントは親から子へ伝播します。
  • テンプレートは高いパフォーマンスを得るため、プリコンパイルされキャッシュされます。
  • 細かい制御のためのライフサイクルイベント
  • アイソモーフィックアプリケーションを実現する、カスタムタグのサーバサイドレンダリング

標準に近い

  • 独自形式のイベントシステムはなし
  • IE8でも使える標準化されたイベント
  • レンダリングされたDOMは、自由に他のツールから操作可能
  • 余計なHTMLのルート要素やdata-属性を使う必要なし
  • jQueryとの親和性が高い

お気に入りのツールと一緒に

  • タグファイルは、CoffeeScriptやJade、Typescript、LiveScript、ES6や、その他の好きなany pre-processorでOK。
  • NPMやCommonJS、AMD、Bower、Componentが使えます。
  • GulpGruntBrowserifyのプラグインでコンパイル

答え2: シンプルでミニマリスト

ミニマリズムが、Riotを他のライブラリと一線を画すものにしています。

楽しい文法

デザインのゴールの一つは、できる限り最小の"boilerplate"で使える、協力な文法を導入することです。

  • 強力なショートカット: class={ enabled: is_enabled, hidden: hasErrors() }
  • 余計なことを考えなくてOK。renderとかstateconstructorshouldComponentUpdateなどなど。
  • インターポレーション: Add #{ items.length + 1 } あるいは class="item { selected: flag }"
  • ロジック部分を<script>タグで囲むのはオプション
  • コンパクトなES6のメソッドの書き方

小さな学習曲線

Riotは他のURライブラリと比較して、10か100倍APIが少ないです。

  1. 覚えることが少ない。見なきゃいけない本もチュートリアルも少ない。
  2. 独自形式なものが少なく、標準的なものが多い

サイズが小さい

react.min.js – 127KB

polymer.min.js – 120KB

riot.min.js – 6.7KB

  1. 少ないバグ
  2. パースが早く、ダウンロードも容易
  3. エンベッダブル(組込可能): ライブラリはアプリケーション本体より小さくあるべき
  4. メンテナンスの手間が少ない: Riotのメンテナンスのために大きなチームを必要としない

小さくて、必要十分

Riotはモダンなクライアントサイドのアプリケーションを作るための、基本的な構成単位をすべて備えています。

  • ユーザインターフェースを構築するための"Reactive"なビュー
  • 分離されたモジュールのAPIを作るためのイベントライブラリ
  • URLと「戻る」ボタンを処理するルータ

Riotは「オープンスタック」です。つまり、フレームワーク特有のイディオムを避けたい開発者向けです。一般的であることで、好きなデザインパターンを適用したり、混ぜたりすることができます。Facebook Fluxのようなシステムをつくることもできます

つまり...?

Riotはサイズは小さいままに、React + Polymer + モデル + ルーティング を実現するライブラリです。今日から使えます。IE8でも。とにかくシンプルで、すごく軽い。車輪の再発明をするのではなく、これらのツールの良いとこ取りで、可能な限りシンプルにしました。

私たちは、テンプレートではなく、再利用可能なコンポーネントにフォーカスするべきです。Reactの開発者曰く:

「テンプレートは、問題ではなく、技術を分けるだけだ」

同じコンポーネントの中で、レイアウトとロジックを一緒に持てば、全体のシステムはより簡潔になります。この重要な洞察について、Reactに敬意を示したいと思います。

Riot 開発者ガイド

カスタムタグの例

Riotのカスタムタグは、ユーザインターフェースの構成要素です。 アプリケーションの「ビュー」部分を担います。Riotのいろいろな特徴をハイライトした、TODOの例の拡張から始めましょう。

<todo>

  <h3>{ opts.title }</h3>

  <ul>
    <li each={ items }>
      <label class={ completed: done }>
        <input type="checkbox" checked={ done } onclick={ parent.toggle }> { title }
      </label>
    </li>
  </ul>

  <form onsubmit={ add }>
    <input name="input" onkeyup={ edit }>
    <button disabled={ !text }>Add #{ items.length + 1 }</button>
  </form>

  <script>
    this.disabled = true

    this.items = opts.items

    edit(e) {
      this.text = e.target.value
    }

    add(e) {
      if (this.text) {
        this.items.push({ title: this.text })
        this.text = this.input.value = ''
      }
    }

    toggle(e) {
      var item = e.item
      item.done = !item.done
      return true
    }
  </script>

<todo>

カスタムタグはJavaScriptにコンパイルされます

ライブデモを見て、そのソースを開くか、ZIPファイルをダウンロードします。

タグの構文

Riotのタグは、レイアウト(HTML)とロジック(JavaScript)の組み合わせです。 基本的なルールは次のとおりです。

  • HTMLが先に定義され、ロジックは<script>タグに書かれます。<script>タグは省略することも可能です。メモ: ドキュメントのbodyに定義を含める場合、scriptタグを必ず省略しなければなりません。scriptタグが使えるのは外部ファイルのみです。
  • <script>タグがない場合、JavaScriptは最後のHTMLタグの直後から始まると見なされます。
  • カスタムタグは、空、HTMLだけ、JavaScriptだけでも可。
  • コーテーションはオプションです: <foo bar={ baz }><foo bar="{ baz }"> に変換されます。
  • ES6のメソッド構文を使えます: methodName()this.methodName = function()に変換され this 変数は常に現在のタグのインスタンスを指します。
  • クラス名のショートカット構文が使えます: class={ completed: done }doneが真のとき、class="completed" としてレンダリングされます。
  • 真偽値属性(checked, selected など)はfalsyな値の場合、無視されます: <input checked={ undefined }><input>.
  • すべての属性名は 小文字 でなければなりません。
  • 自己終了タグがサポートされています: <div/><div></div> と等しくなります。 いわゆる「オープンタグ」 <br><hr><img><input>はコンパイルの後に閉じられることはありません。
  • カスタムタグは常に閉じられている必要があります。(通常通り、あるいは自己終了タグとして)
  • 標準のHTMLタグ(labeltableaなど)もカスタムタグ化することができますが、あまり良い手ではありません。

タグファイル内のタグ定義は常に行の先頭から書き始めます。

<!-- works -->
<my-tag>

</my-tag>

<!-- also works -->
<my-tag></my-tag>

  <!-- this fails, because of indentation -->
  <my-tag>

  </my-tag>

インラインのタグ定義(ドキュメントのbody内)は正しくインデントされていなくてはなりません。すべてのカスタムタグは一番インデントの小さい行に揃える必要があります。タブとスペースも混ぜるべきではありません。

Scriptタグの省略

<script>タグは省略することができます:

<todo>

  <!-- layout -->
  <h3>{ opts.title }</h3>

  // logic comes here
  this.items = [1, 2, 3]

<todo>

その場合、ロジックは最後のHTMLタグの後に開始されます。 この「オープン構文」は、Webサイト上での例示でよく使われます。

プリプロセッサ

type属性で、プリプロセッサを指定できます。例えば次のようになります。

<script type="coffee">
  # your coffeescript logic goes here
</script>

現在のところ、"coffee"と"typescript"、"es6"、"none"を使うことができます。言語指定で"text/"を接頭辞としてつけて、"text/coffee"言語指定に"text/"を接頭辞としてつけ、"text/coffee"のようにしても構いません。

詳細については プリプロセッサを参照してください。

タグのスタイリング

styleタグを含めることができます。Riot.jsは自動的にその内容を<head>の最後に挿入します。

<todo>

  <!-- layout -->
  <h3>{ opts.title }</h3>

  <style>
    todo { display: block }
    todo h3 { font-size: 120% }
    /** other tag specific styles **/
  </style>

<todo>

Scoped CSS

Scoped CSS も利用可能です。次の例は最初のものと等価です。

<todo>

  <!-- layout -->
  <h3>{ opts.title }</h3>

  <style scoped>
    :scope { display: block }
    h3 { font-size: 120% }
    /** other tag specific styles **/
  </style>

<todo>

スタイルの挿入は一度だけ行われます。タグが何度使われたかは関係ありません。

Riotが挿入したCSSを上書きしたい場合、<head>の中でCSSの挿入位置を指定することが可能です。

<style type="riot"></style>

例えば、(1)normalize.cssの後に、(2)コンポーネントライブラリのタグ内のスタイルが挿入され、(3)WebサイトのテーマCSSがデフォルトスタイルを上書きするような、ユースケースが考えられます。

タグのマウント

タグを作成したら、次のように、ページ上でそれをマウントすることができます。

<body>

  <!-- place the custom tag anywhere inside the body -->
  <todo></todo>

  <!-- include riot.js -->
  <script src="riot.min.js"></script>

  <!-- include the tag -->
  <script src="todo.js" type="riot/tag"></script>

  <!-- mount the tag -->
  <script>riot.mount('todo')</script>

</body>

ページのbody内のカスタムタグは<todo></todo>のように閉じられる必要があります。自己終了タグ<todo/>はサポートしません。

マウントメソッドの使用例をいくつか示します。

// mount all custom tags on the page
riot.mount('*')

// mount an element with a specific id
riot.mount('#my-element')

// mount selected elements
riot.mount('todo, forum, comments')

文書には、同じタグの複数のインスタンスを含めることができます。

オプション

第二引数にタグのオプションを渡すことができます。

<script>
riot.mount('todo', { title: 'My TODO app', items: [ ... ] })
</script>

渡すデータはなんでも構いません。シンプルなオブジェクトから、フルアプリケーションのAPIまで。あるいは、Fluxのストアを渡すのも手です。アーキテクチャのデザイン次第です。

次のようにタグ内のオプションは、opts変数で参照することができます。

<my-tag>

  <!-- Options in HTML -->
  <h3>{ opts.title }</h3>

  // Options in JavaScript
  var title = opts.title

</my-tag>

ミックスイン

ミックスインは、タグを超えての機能を共有するための簡単​​な方法を提供します。 タグはRiotによって初期化されると、ミックスインが追加され、タグの中から使用できるようになります。

var OptsMixin = {
    init: function() {
      this.on('updated', function() { console.log('Updated!') })
    }

    getOpts: function() {
        return this.opts
    },

    setOpts: function(opts, update) {
        this.opts = opts

        if(!update) {
            this.update()
        }

        return this
    }
}

<my-tag>
  <h3>{ opts.title }</h3>

    this.mixin(OptsMixin)
</my-tag>

この例では、どのmy-tagタグのインスタンスに対しても、getOptssetOptsを提供するOptsMixinミックスインを与えています。initは特別なメソッドで、タグに読み込まれる際にミックスインを初期化できます。(initは、ほかのメソッドからはアクセスできません)

var my_tag_instance = riot.mount('my-tag')[0]

console.log(my_tag_instance.getOpts()) //will log out any opts that the tag has

タグは(ミックスインとして)どんなオブジェクトも受け入れます。{'key': 'val'}var mix = new function(...)など。一方、それ以外の型が与えられた場合はエラーとなります。

これで、my-tagタグの定義には、OptsMixinに定義されたほかのものと一緒に、getIdメソッドが含まれるようになりました。

function IdMixin() {
    this.getId = function() {
        return this._id
    }
}

var id_mixin_instance = new IdMixin()

<my-tag>
  <h3>{ opts.title }</h3>

    this.mixin(OptsMixin, id_mixin_instance)
</my-tag>

このようにタグ内で指定されることで、ミックスインは、単にタグの機能を拡張するだけでなく、繰り返し利用可能なインターフェイスを提供します。 タグがマウントされるたびに、内部のタグも、インスタンスはそれぞれのミックスインのコードを持つことになります。

ミックスインの共有

ミックスインをファイルやプロジェクトを超えて共有するために、riot.mixin APIが用意されています。あなたのミックスインをグローバルに登録するには次のようにします。

riot.mixin('mixinName', mixinObject)

このミックスインをタグにロードするには、キーを指定してmixin()メソッドを使います。

<my-tag>
  <h3>{ opts.title }</h3>

    this.mixin('mixinName')
</my-tag>

タグのライフサイクル

タグは次の一連の流れで作成されます。

  1. タグが構成される
  2. タグのJavaScriptロジックが実行される
  3. テンプレート変数が計算され、"update"イベントが発火
  4. ページ上でタグがマウントされ、"mount"イベントが発火

タグがマウントされた後、テンプレート変数は次のように更新されます。

  1. イベントハンドラが呼び出された際に自動的に(イベントハンドラ内で、e.preventUpdatetrueにセットしない場合)。例えば、最初の例のtoggleメソッド。
  2. this.update()が現在のタグインスタンス上で呼ばれたとき
  3. this.update()が親タグあるいは、さらに上流のタグで呼ばれたとき。更新は親から子への一方通行で流れる。
  4. riot.update()が呼ばれたとき。ページ上のすべてのテンプレート変数を更新。

タグが更新されるたびに、"update"イベントが発火します。

値はマウント以前に計算されるため、<img src={ src }>という呼び出しが失敗するような心配はありません。

ライフサイクルイベント

次のような手順で、様々なライフサイクルイベントについてタグの中からリスナー登録することができます。

<todo>

  this.on('mount', function() {
    // right after tag is mounted on the page
  })

  this.on('update', function() {
    // allows recalculation of context data before the update
  })

  this.on('unmount', function() {
    // when the tag is removed from the page
  })

  // curious about all events ?
  this.on('mount update unmount', function(eventName) {
    console.info(eventName)
  })

<todo>

ひとつのイベントに複数のリスナーを登録することも可能です。イベントの詳細については、observableを参照してください。

テンプレート変数 (expressions)

HTMLには、括弧で囲まれたテンプレート変数を挿入することができます。

{ /* my_expression goes here */ }

Expressions can set attributes or nested text nodes:

<h3 id={ /* attribute_expression */ }>
  { /* nested_expression */ }
</h3>

テンプレート変数は 100% JavaScript です。 いくつか例を示します:

{ title || '名称未設定' }
{ results ? '準備OK!' : '読み込み中...' }
{ new Date() }
{ message.length > 140 && 'メッセージが長すぎます' }
{ Math.round(rating) }

ゴールはテンプレート変数を小さく保ってHTMLを可能な限りクリーンに保つことです。もし、テンプレート変数が複雑になるようであれば、ロジックを"update"イベントに移すことを検討しましょう。例:

<my-tag>

  <!-- the `val` is calculated below .. -->
  <p>{ val }</p>

  // ..on every update
  this.on('update', function() {
    this.val = some / complex * expression ^ here
  })
</my-tag>

真偽値属性

真偽値属性 (checked, selected など) はテンプレート変数がfalse的であれば無視されます。

<input checked={ null }><input> になります。

W3Cは真偽値属性が存在する場合は、例えその値がfalseだとしてもtrueとするとしています。

次の書き方では、うまく動きません:

<input type="checkbox" { true ? 'checked' : ''}>

属性と値のみがテンプレート変数として許されるためです。Riotは44の異なる真偽値属性を判別します。(checked, disabled など)

クラス省略記法

RiotはCSSクラス名について特別な文法をもっています。例えば、

<p class={ foo: true, bar: 0, baz: new Date(), zorro: 'a value' }></p>

は、"foo baz zorro"として評価されます。その値が真になるプロパティ名は、クラス名のリストに追加されます。もちろん、この表記法はクラス名以外の場所で使うこともできます。もしふさわしい使い場所があれば。

括弧の表示

開始括弧をエスケープすれば、評価せずにテンプレート変数をすのまま表示することができます:

\\{ this is not evaluated \\} outputs { this is not evaluated }

括弧のカスタマイズ

括弧を好きなものにカスタマイズするのは自由です。たとえば、このようにできます。

riot.settings.brackets = '${ }'
riot.settings.brackets = '\{\{ }}'

開始と終了はスペースで区切られています。

プリコンパイラ0 使う際は、同じく括弧オプションを設定する必要があります。

その他

styleタグの中の波括弧は、テンプレート変数として評価されません。

エスケープしないでHTMLを表示する

Riotのテンプレート変数は、HTML形式を含まないテキストのみ表示可能です。しかし、そのためのカスタムタグを作成することはできます。例えば、

<raw>
  <span></span>

  this.root.innerHTML = opts.content
</raw>

このようなタグを定義しておけば、他のタグの中から利用することができます。こんな感じです。

<my-tag>
  <p>Here is some raw content: <raw content="{ html }"/> </p>

  this.html = 'Hello, <strong>world!</strong>'
</my-tag>

jsfiddle上のデモ

警告 これはユーザをXSS攻撃の危険にさらす場合があります。信用できないソースからのデータを、絶対にロードしないようにしなくてはなりません。

入れ子のタグ

親タグ<account>と入れ子になったタグ<subscription>を定義しましょう:

<account>
  <subscription  plan={ opts.plan } show_details="true" />
</account>


<subscription>
  <h3>{ opts.plan.name }</h3>

  // Get JS handle to options
  var plan = opts.plan,
      show_details = opts.show_details

  // access to the parent tag
  var parent = this.parent

</subscription>

それでは、accountタグを plan設定オプションとともに、ページにマウントします:

<body>
  <account></account>
</body>

<script>
riot.mount('account', { plan: { name: 'small', term: 'monthly' } })
</script>

親タグのオプションはriot.mountメソッドともに渡され、子タグのオプションはタグ属性として渡されます。

重要 入れ子タグは必ず親タグの中で宣言されます。ページに定義されていても初期化されません。(訳注: riot.mountで呼んでいるのは、親タグだけだから)

入れ子のHTML

「HTMLトランスクルージョン」は、カスタムタグ内のHTMLを処理する方法のひとつです。これは、ビルトインの<yield>タグによって実現します。次はその例です。

タグの定義

<my-tag>
  <p>Hello <yield/></p>
  this.text = 'world'
</my-tag>

使い方

カスタムタグはページ内に入れ子にされたHTMLとともに配置されます。

<my-tag>
  <b>{ text }</b>
</my-tag>

表示結果

<my-tag>
  <p>Hello <b>world</b><p>
</my-tag>

yieldの詳細については、APIドキュメントを参照してください。

名前付き要素

nameまたはid属性のある要素は、JavaScriptから簡単にアクセスできるよう、自動的にコンテキストに追加されます。

<login>
  <form id="login" onsubmit={ submit }>
    <input name="username">
    <input name="password">
    <button name="submit">
  </form>

  // grab above HTML elements
  var form = this.login,
    username = this.username.value,
    password = this.password.value,
    button = this.submit

</login>

もちろん、これらの名前付き要素はHTMLの中のテンプレート変数からも参照できます: <div>{ username.value }</div>

イベントハンドラ

DOMイベントを扱う関数は「イベントハンドラ」と呼ばれます。イベントハンドラは次のように定義されます。

<login>
  <form onsubmit={ submit }>

  </form>

  // this method is called when above form is submitted
  submit(e) {

  }
</login>

「on」で始まる属性(onclickonsubmitoninputなど)には、イベントが起きた際に呼ばれる関数を設定できます。この関数はテンプレート変数によって動的に定義されることも可能です。例:

<form onsubmit={ condition ? method_a : method_b }>

この関数の中でthisは現在のタグのインスタンスを参照しています。ハンドラが呼ばれた後、this.update()が自動的に呼ばれ、加えられた変更がUIに反映されます。

デフォルトのイベントハンドラの挙動は、チェックボックスかラジオボタンでなければ、自動的にキャンセル です。つまり、あなたのためにe.preventDefault()は実行済みです。これはキャセルすることがほとんどだからです(しかも、忘れがち)。もし、ブラウザのデフォルトの挙動のままにしたい場合は、ハンドラでtrueを返してください。

例えば、このsubmitハンドラは、実際にサーバへフォームを送信します。

submit() {
  return true
}

イベントオブジェクト

イベントハンドラは通常のイベントオブジェクトを第一引数に受け取ります。次のプロパティについては、ブラウザが異なっても動作するよう標準化されています。

  • e.currentTargetは、イベントハンドラが指定された要素を指します
  • e.targetはイベントの送信元エレメントです。これは必ずしも必要ではなく、currentTargetと同じです。
  • e.whichはキーボードイベント(keypresskeyupなど)のキーコートです。
  • e.itemはループの中でのみ有効で、現在の要素を指します。詳しくはループを参照してください。

条件属性

条件属性を使うと、条件によって要素を表示/非表示できます。例:

<div if={ is_premium }>
  <p>This is for premium users only</p>
</div>

繰り返しになりますが、テンプレート変数はシンプルなプロパティでも、フルなJavaScriptでも構いません。次の特別な属性が利用できます。

  • show – 真のときに、style="display: ''"として要素を表示します。
  • hide – 真のときに、style="display: none"として要素を非表示にします。
  • if – ドキュメントの要素を、追加(真のとき)あるいは削除(偽のとき)します

等号には==を使い、===は使いません。たとえば、'a string' == trueのような書き方はOKです。

ループ

次のようにループはeach属性として実装されています。

<todo>
  <ul>
    <li each={ items } class={ completed: done }>
      <input type="checkbox" checked={ done }> { title }
    </li>
  </ul>

  this.items = [
    { title: 'First item', done: true },
    { title: 'Second item' },
    { title: 'Third item' }
  ]
<todo>

each属性を持った要素は配列の要素の数だけ繰り返されます。例えば、配列がpush()slice()あるいはspliceメソッドで操作された場合、自動的に新しい要素が追加/生成されます。

コンテキスト

新しいコンテキストが配列の要素ごとに作られ、その親にはparent変数を通じてアクセスできます。例:

<todo>
  <div each={ items }>
    <h3>{ title }</h3>
    <a onclick={ parent.remove }>Remove</a>
  </div>

  this.items = [ { title: 'First' }, { title: 'Second' } ]

  remove(event) {

  }
<todo>

ループ要素では、each属性以外のすべては子コンテキストに紐付きます。そのため、上の例ではtitleには直接アクセスできるのに対して、removeはループ要素のプロパティではないため、parent.がないとアクセスできません。

ループ要素はタグインスタンスです。Riotはもとの要素にタッチしないので、新しいプロパティが付け加えられることもありません。

ループとイベントハンドラ

イベントハンドラは配列の中の個別の要素に、event.itemでアクセスできます。では、remove関数を実装することを考えてみます:

<todo>
  <div each={ items }>
    <h3>{ title }</h3>
    <a onclick={ parent.remove }>Remove</a>
  </div>

  this.items = [ { title: 'First' }, { title: 'Second' } ]

  remove(event) {

    // ループ要素
    var item = event.item

    // 配列の中のインデックス
    var index = this.items.indexOf(item)

    // 配列から削除
    this.items.splice(index, 1)
  }
<todo>

イベントハンドラが実行された後、対象のタグインスタンスはthis.update()を使って更新されます。(イベントハンドラの中で、e.preventUpdatetrueにセットしない限り)親要素は、配列から要素が削除されたことを検知して、該当するDOM要素をドキュメントから削除します。

カスタムタグのループ

カスタムタグもループさせることができます。例:

<todo-item each="{ items }" data="{ this }"></todo-item>

現在のループ要素はthisで参照して、ループされたタグにオプションとして渡すことができます。

非オブジェクト配列

配列の要素がオブジェクトである必要はありません。文字列や数でも構いません。そのケースでは、次のように{ name, i in items }を使ってループにします。

<my-tag>
  <p each="{ name, i in arr }">{ i }: { name }</p>

  this.arr = [ true, 110, Math.random(), 'fourth']
</my-tag>

nameは要素そのものでiがインデックス番号です。どちらも、状況に合わせてどんなラベルでも構いません。

オブジェクトのループ

オブジェクトもループにすることができます。例:

<my-tag>
  <p each="{ name, value in obj }">{ name } = { value }</p>

  this.obj = {
    key1: 'value1',
    key2: 1110.8900,
    key3: Math.random()
  }
</my-tag>

内部的にRiotはJSON.stringifyで変更検知をしているため、オブジェクトループは推奨されていません。オブジェクト全体として調べられ、変更が見つかると全体を再描画してしまいます。これは、動作が遅くなる原因になりえます。通常の配列は、変更箇所だけが再描画されるためもっと速いです。

標準のHTML要素にレンダリング | #riot-tag

標準HTMLも、riot-tag属性を付けることでページ内のカスタムタグとして利用できます。

<ul riot-tag="my-tag"></ul>

このことは、CSSフレームワークとの互換性を持つ代替手段をユーザに提供しています。タグはほかのカスタムタグと同様に扱われます。

riot.mount('my-tag')

は、上に示したul要素を、あたかも<my-tag></my-tag>かのようにマウントします。

サーバサイドレンダリング | #server-side

Riotはサーバサイドレンダリングをサポートします。Node/io.js上で、タグをrequireしてHTMLにレンダリングすることができます:

var riot = require('riot')
var timer = require('timer.tag')

var html = riot.render(timer, { start: 42 })

console.log(html) // <timer><p>Seconds Elapsed: 42</p></timer>

ループと、条件属性がサポートされています。

アプリケーション設計

ポリシーではなく、ツール

Riotには、カスタムタグとイベントシステム(observable)、ルーターがバンドルされています。これらが、クライアントサイドアプリケーションを構築するために必要な、最も基本的な要素だと考えています。

  1. カスタムタグ: ユーザインターフェースのため
  2. イベントシステム: モジュール性のために
  3. ルーター: URLと「戻るボタン」のため

Riotは、厳格なルールを押しつけるよりも、創造性を発揮するためのベーシックなツールだけを提供します。この柔軟なアプローチにより、開発者に設計上の選択の余地を大きく残しています。

また、なるべくファイルサイズとAPIの数という点でも、基本要素はミニマルであるべきだと考えています。基本的なものはシンプルであるべきで、そうであれば認知的な負荷も最低限で済みます。

Observable

Observableはイベントを送ったり受け取ったりするための汎用的なツールです。依存性や「結合」を避けてモジュールを分離するために、よく使われるパターンのひとつです。イベントを使うことで、大きなプログラムは小さく簡単なユニットに分割できます。モジュールは追加することも、削除することも、アプリケーションの他の部分に影響を与えずに変更することもできます。

よくあるやりかたは、アプリケーションをひとつのコアと複数のエクステンションに分けることです。コアは何かが起きるとイベントを送ります。新しいアイテムが追加されたり、既存のアイテムが削除されたり、あるいはサーバから何かが読み込まれたり。

Observableを使うことで、エクステンションはイベントを検知して、それらに反応することができるようになります。コアが関知していなくても、コアを拡張できるわけです。これを「疎結合」と言います。

これらのエクステンションはカスタムタグ(UIコンポーネント)の場合も、非UIモジュールの場合もあります。

一度コアとイベントを注意深くデザインしてしまえば、開発チームのメンバーは他の部分に煩わされずにシステムの開発を進めることができます。

Observable API

ルーティング

ルーターはURLと「戻るボタン」を扱うための、汎用的なツールです。Riotのルーターは、IE8を含むすべてのブラウザで動かすための、最小の実装になっています。次のことが可能です:

  1. URLのハッシュ部分を変更
  2. ハッシュの変更を通知
  3. 現在のハッシュを調べる

ルーティングのロジックはどこにでも置くことができます。カスタムタグでも、非UIモジュールでも構いません。ルーターを、各部分に指示を出すための、アプリケーションの中心的な要素と位置付けるアプリケーションフレームワークもあります。一方で、URLイベントをキーボートイベントと同じように扱って、アプリケーションの全体的なアーキテクチャを変えないというアプローチもあります。(訳注: どういった設計にするかは、開発者に委ねられています)

ロケーションバーには常にURLが表示されているわけですから、どんなブラウザアプリケーションにもルーティングは必要です。

ルーターAPI

モジュール性

カスタムタグはアプリケーションのビュー部分を作ります。モジュール化されたアプリケーションでは、これらのタグは互いについて関知せず、分離されているべきです。理想的には、外側のHTMLレイアウトにかかわらず、同じタグを別プロジェクトでも使えるはずです。

もし、2つのタグがお互いを「知って」いると、互いに依存した「密結合」を呼び込んでしまいます。そうなると、システムを壊さずにタグを自由に動かすことができなくなります。

結合を避けるには、互いを直接呼び出すよりもイベントに登録するようにします。必要なのは、riot.observableかそれと同等のpub/subシステムです。

このイベントシステムは、シンプルにAPIから、Facebook Fluxのようなより大きな設計にまで対応できます。

Riotアプリケーションの設計例

これは、ユーザログインを実現するための、非常に簡略化したRiotアプリケーションの骨格です。

// Login API
var auth = riot.observable()

auth.login = function(params) {
  $.get('/api', params, function(json) {
    auth.trigger('login', json)
  })
}


<!-- login view -->
<login>
  <form onsubmit="{ login }">
    <input name="username" type="text" placeholder="username">
    <input name="password" type="password" placeholder="password">
  </form>

  login() {
    opts.login({
      username: this.username.value,
      password: this.password.value
    })
  }

  // any tag on the system can listen to login event
  opts.on('login', function() {
    $(body).addClass('logged')
  })
</login>

そして、アプリケーションをマウントします。

<body>
  <login></login>
  <script>riot.mount('login', auth)</script>
</body>

上記のセットアップでは、システムは互いを知っている必要がありません。シンプルに「ログイン」イベントを検知して、それぞれに求められたことを果たします。

Observableは疎結合な(モジュール化された)アプリケーションのための、クラシックな構築要素です。