Riot vs React & Polymer
Riotとその他近しいライブラリは何が違うの?
React
Riotは、Reactとその「まとめ方(cohesion)」のアイデアからインスパイアされました。Facebookの開発者いわく:
「テンプレートは、問題ではなく、技術を分けるだけだ」
“Templates separate technologies, not concerns.”
私たちは、この直感に敬意を表します。ゴールはテンプレートを作ることではなく、再利用可能なコンポーネントを作ることです。ロジックをテンプレートから分離することで、本来一緒にしておくべきものを追い出してしまっているのです。
これらの関連技術をコンポーネント内にまとめることで、システムはよりクリーンになります。この重要な直感において、Reactは偉大です。
Reactの文法
import React from 'react'
import { render } from 'react-dom'
class Todo extends React.Component {
state = { items: [], value: '' }
handleSubmit = e =>
e.preventDefault() || this.setState({ items: [...this.state.items, this.state.value], value: '' })
handleChange = e => this.setState({ value: e.target.value })
render() {
return (
<div>
<h3>TODO</h3>
<ul>
{this.state.items.map((item, i) => <li key={i}>{item}</li>)}
</ul>
<form onSubmit={this.handleSubmit}>
<input value={this.state.value} onChange={this.handleChange} />
<button>Add #{this.state.items.length + 1}</button>
</form>
</div>
)
}
}
render(<Todo />, mountNode)
JSXはHTMLとJavaScriptが混在しています。HTMLをコンポーネントの好きなところに含めることができます。メソッド内でも、プロパティの値としても。
Riotの文法
こちらは上と同じ内容をRiotで書いた場合です:
<todo>
<h3>TODO</h3>
<ul>
<li each={ item, i in items }>{ item }</li>
</ul>
<form onsubmit={ handleSubmit }>
<input ref="input">
<button>Add #{ items.length + 1 }</button>
</form>
this.items = []
handleSubmit(e) {
e.preventDefault()
var input = this.refs.input
this.items.push(input.value)
input.value = ''
}
</todo>
そして、これは上記のタグがどのようにページ上にマウントされるかを表しています:
<todo></todo>
<script>riot.mount('todo')</script>
同じ、だけど全然違う
Riotでは、HTMLとJavaScriptはより見慣れた形であらわれます。どちらも、同じコンポーネント下にありますが、きちんとそれぞれが分けられています。HTMLはJavaScriptのテンプレート変数(expressions)と混ぜることができます。
テンプレート変数を波括弧で囲むこと以外、独自路線は一切なしです。
少ないボイラープレート、少ない括弧、カンマ、システムプロパティやメソッド名に気がつくでしょう。文字列には、変数を挿入することができます: "Hello " + this.state.world
の代わりに"Hello {world}"
、そしてメソッドはES6のコンパクトな文法で定義できます。ほんの少しだけです。
Riotの文法は、再利用可能なコンポーネントとして分離する利点を体感しつつ、レイアウトとロジックを分ける一番すっきりした方法だと、私たちは考えています。
仮想DOM vs テンプレート変数バインディング (expressions binding)
コンポーネントが初期化される際、Reactは仮想DOMを作りますが、一方でRiotはDOMツリーをトラバースするのみです。
Riotはテンプレート変数をそのツリーから取得し、配列に保持します。それぞれのテンプレート変数は、DOMノードへのポインターを持っています。実行ごとにこれらのテンプレート変数は評価され、DOMの値と比較されます。値が変更されていれば、該当するDOMノードが更新されます。
これらのテンプレート変数はキャッシュされるため、更新は非常に高速です。100または1000の式を実行するには、通常1ミリ秒かそれ以下です。
Reactの場合、更新ごとにHTMLレイアウトがランダムに変更されうるため、同期アルゴリズムはもっと複雑です。この計り知れない挑戦に、Facebookの開発者たちは素晴らしい仕事をしました。
しかし、この複雑な変更検知(diffing)を避けられることを、私たちはすでに見てきました。
RiotではHTMLの構造は固定されています。ループと条件文だけが、要素の追加と削除を行います。ですが、例えばdiv
がlabel
に変換されるようなことは起きえません。Riotは複雑な部分木(DOMツリー)の置き換えなしに、テンプレート変数だけを更新します。
Fluxとルーティング
ReactはUIのみを扱いますが、それ自体は良いことです。偉大なソフトウェアプロジェクトは必ず鋭いフォーカスを持っています。
Facebookはクライアントサイドのコードを構造化するのに、Fluxの利用を推奨しています。これはフレームワークというよりも素晴らしいアイデアを詰め込んだ、ひとつのパターンです。
Riotはカスタムタグとともに、イベントエミッタ(オブザーバブル)とオプショナルなルータがついて来ます。私たちは、これらがクライアントサイドアプリケーション構築の基礎的なブロックだと信じています。イベントはモジュール性をもたらし、ルータはURLと「戻る」ボタンをハンドリングし、カスタムタグがユーザインターフェースを担います。
まさにFluxのように、Riotは柔軟で、開発者に設計上の大きな決定権を残しています。これは、ゴールに到達するのを助けるライブラリにすぎません。
RiotのオブザーバブルとルータでFluxライクなシステムを構築することも可能です。実際、そういった試みもすでにあります。
3倍大きい
React(v16.4.0)は、Riotの3倍のサイズです。
react.min.js – 33.27KB (gzip)
riot.min.js – 10.85KB (gzip)
Reactの推奨ルータ(v4.1.1)は、Riotのルータの6倍です。
react-router.min.js – 10.95KB (gzip)
react-mini-router.min.js – 4.52KB (gzip)
riot.router.min.js – 1.77KB (gzip)
確かに、このルータ比較はちょっと不公平です。なぜなら、react-routerはより多くの機能を持っています。ですが、この図はRiotのゴール、つまり「最もミニマリスティックなAPIを提供すること」を明確に示しています。
Reactのエコシステムはよりフレームワーク的で、APIの肥大化の気配がします。実際、react-mini-routerよりも、この大きな選択肢の方がReactコミュニティでは人気です。
Polymer
PolymerはWeb Component標準に則っており、最新ブラウザで利用可能にします。これは、カスタムタグを標準的な方法で書けるということです。
コンセプトとしてはRiotも同じなのですが、いくつかの違いがあります:
-
Web Componentsの文法は、実験的で複雑です。
-
Riotは変更のあった要素だけを更新するため、少ないDOM操作で済みます。
-
それぞれのコンポーネントはHTMLの
link rel="import"
で読み込まれます。PolyfillsはXHRsに頼る必要があり、専用のvulcanizeツールを使わない限り、耐えられない遅さです。Riotのタグはscript src
で読み込まれ、一般的なツールで複数のタグを結合することができます。 -
サーバサイドレンダリングができません。
6倍大きい
Polymer(v1.8.0) + WebComponents(v0.7.24)はRiotの6倍のサイズです。
polymer.min.js – 49.38KB (gzip)
riot.min.js – 10.85KB (gzip)
Web ComponentsはPolyfillの課題の王様と呼ばれ、Polymerがこんなにも巨大なコードを必要とする所以です。
Web components
Web Componentsが標準であるため、最終的にはここに辿り着くべきです。それには年月を必要としますが、いずれは標準コンポーネントがwebを満たすことになるでしょう。
複雑さを伴うため、これらのコンポーネントが直接使われない可能性が高くなります。今日多くの人が、直接DOMを操作せずjQueryを使うように、Web Componentsの上にもレイヤーが存在するようになるでしょう。
Riotはそういった抽象化の一層です。それはアプリケーションが頼りにできる簡単なAPIを提供します。Web Componentsの仕様が発達するにつれ、パフォーマンス向上など実際のメリットがあるのであれば、Riotは内部でそれらを導入することができます。
Riotの目的はUI開発を可能な限り簡単にすることです。現在のAPIは、ウェブテクノロジーの日進月歩の流転に耐えうるよう設計されています。Web ComponentsのjQueryと見てもいいでしょう。より簡潔な文法で同じ目的を果たすからです。それは再利用可能なコンポーネントの作成をシンプルな体験にします。