ES6、React、RxJS、Cycle.js に関する調査

  • 222
    いいね
  • 1
    コメント

React と Cycle.js を学び始めると、Twitter のつぶやきを含めて、こまごまごしたことを調べる必要があり、記憶力の限界を超えたので、自分の関心の変遷を振り返りながら、情報源をまとめました。ES6 の実装の課題とおよび将来の ES.next の標準化の動きについても併記しました。

プログラミングスタイル

新しい機能をなぜ導入するのかについて振り返るために QCon Tokyo 2013 の Douglas Crockford 氏の講演を文字に起こした記事 (前編後編)を読んで印象に残ったことを抜粋しました。

スタイルは間違いを減らすためにある

  • スタイルは個人の嗜好や自己表現のためではなく、間違いを減らすためにある。
  • わるいスタイルを採用する人の典型例は勉強不足な人、古い教育を受けた人、スリルを好む人、自己顕示欲家。
  • めったに起きないと思っていることはいずれ起きる。
  • エレガントなコードはぱっと見てわかりにくく、一部を変更すると全体が壊れてしまうので、メンテナンスがむずかしい。

信頼できるサブセットだけを使う

  • 言語のすべての機能を使うのではなく、その中で信頼できるサブセットだけを使う。
  • プログラマは自分の意志で機能を使わないことを選ぶことができる。
  • 間違いを起こしやすい機能がいったん言語に取り込まれると削除することがむずかしい。

ES2015 (ES6)

Babel と Webpack のセットアップ

Babel 6 になり、プリセットとプラグインのセットアップが必要になりました。セットアップの取り組みに関してこちらの記事をご参照ください。

ES2015 のすべての機能を使う必要はない

後述する Babel や TypeScript などのトランスパイラーを使う場合の悩ましい問題は、コードの分量が大きくなるほど、変換のための処理時間が長くなることです。

複数のブラウザーに対応するために、最終的にトランスパイラーは必要ですが、Node.js (v8) やヘッドレスブラウザー (PhantomJS、Nightmare) がサポートする機能の範囲(ECMAScript 2015 (ES6) in Node.js)にとどめておけば、ちょっとしたアイディアを試したり、テストコードなどをターミナルで実行する際に変換しなくてすむ分だけ、作業効率を落とさなくてすみます。

3年後にコードを維持する人の状況を想像する

トランスパイル、圧縮、テストなどのためにさまざまなツールを導入すると、ちょっとしたコードを書くこともめんどうに感じて、古い書き方でよいのではないかと思うことがあります。その場合、3年後に自分が書いたコードを維持する人の状況を想像するとよいのではないでしょうか。

たとえば、2018年に ES5 から ES2018 対応のコードに書き換えることに追われるのか、それとも ES2015 から ES2018 への対応に追われるのではモチベーションが異なるでしょう。

const、let の推奨

Airbnb のコーディングスタイルでは constlet の利用を推奨しています。

let を使えば変数のスコープを var よりも限定的にすることができます。

for (let i = 0; i < 10; i++) {
  console.log(i); // 0, 1, 2, 3, 4 ... 9
}

console.log(i); // i is not defined

クラス導入の是非

ES6 では class 構文が導入され、function 構文を使う書き方よりも意図がわかりやすくなりました。一方で、「Programming JavaScript Applications」の著者である Eric Elliott 氏は new、this、instanceof、super の問題についてまとめ、クラスを廃止すべきであると主張しています (How to Fix the ES6 class keyword)。Eric 氏はオブジェクトの合成をより柔軟にできるように Stamp を開発しています。

Axel 氏は ES6 のクラスはコンポジション、モジュール、プロトタイプ OO よりもベターな選択肢であるかどうかを議論しようとは思わないことを述べ、ES6 のクラスはエレガントではないが、もっともプラグマティックな選択肢と述べています。Redux 開発者の Dan 氏はHow to Use Classes and Sleep at Night のなかで、フレームワークごとに独自のクラスシステムやミックスインを学ばなければならない状況よりもよいことを挙げています。

CoffeeScript への見切りと ES6 への移行ガイド

2015年は ES6 の注目が高まる一方で、CoffeeScript の利用に見切りをつけたり (さよなら CoffeeScript)、CoffeeScript のコードを ES6 に移行する動きが見られました (Moving to ES6 from CoffeeScript)。

Backbone.js での ES6 クラスの実践

Backbone and ES6 Classes RevisitedLessons Backbone Developers Can Learn From React の記事が参考になります。

ES.Next

後述する RxJS への関心の高まりが EcmaScript の仕様に影響を及ぼすようになっています。

Observable の提案

Observable は EcmaScript への導入が提案されています。RxJS の開発者である Taylor 氏は ES6 の Promise が Future から逸脱していることにより、発見や再現が困難もしくは不可能な新しいクラスのバグと競合条件がもたらされたと述べ、Observable で同じ過ちを繰り返すべきではないと述べています (ソース)。

Object.observe の却下

2015年11月に入り、ES7 で提案されていた Object.observe が却下になりました。Ember と Angular などのデータバインディングフレームワークが興味を示すものの、既存のモデルと調和させることがむずかしく、React のようなミュータブルな状態の継承を避けるための取り組みが急速に人気を得ていることが挙げられています。(An update on Object.observe)。

ES2017 で async/await 導入

async/await 文が導入され、try/catch 文のなかで Promise を使うことができるようになりました。HTTP リクエストの例はこちらの記事をご参照ください。

Virtual DOM

DOM 操作は遅いのか

React をライブラリを使い始める前、わざわざ抽象化レイヤーを設置するほど DOM の操作は遅いのかという素朴な疑問がありました。次の記事では DOM 操作のうち、どのフェーズで処理が遅くなるのかが解説されています。

Virtual DOM の代替案

Incremental DOM

Virtual DOM の代替案として Incremental DOM が Google のリポジトリで公開されました。Google Developers の解説記事(日本語訳)によると、開発の動機として、メモリ使用量の削減とテンプレート言語での利用が挙げられています。

React

jQuery で React のアイデアを学ぶ

jQuery で書かれた大量のコードがあり、React を導入することを検討している場合、React のアイデア (状態オブジェクト、純粋な関数、仮想 DOM) を取り込んでリファクタリングすることで、学習と事前準備することができます (ReactをjQueryの数行に要約する)。

ステートレスに向かうコンポーネント

React を学び始めたころ、コンポーネントのプロパティで状態の管理を積極的に扱うべきなのかと思っていましたが、いろいろ調べると、逆の方向にあるようです。

まず、v0.14 でステートレス関数コンポーネントのための構文がサポートされました。リリース記事によると、理想的には、大半のコンポーネントはステートレスで、単にほかのコンポーネントを合成するものとのことです。
さらに props オブジェクトはフローズンになり、コンポーネント生成後の変更はサポートされなくなりました。また、setProps と replaceProps、cloneWithProps は非推奨になりました。

サンプルコードを見ると、EcmaScript 6 のアロー関数が使われており、関数型言語のように見えます。

var Aquarium = (props) => {
  var fish = getFish(props.species);
  return <Tank>{fish}</Tank>;
};

Recompose は関数コンポーネントの合成を支援します。

RxJS、Bacon.js、Kefir との連携

後述の RxJS との連携を目指す rx-recompose が開発されています。Containers Are Dead. Long Live Observable Combinators では Bacon.js との組み合わせが示されています。React Combinators では Kefir との組み合わせが示されています。

this-less コンポーネント/関数

ステートレスと並行して、react-mini のように「this-less」 という表現を目にするようになりました。

High-Order Component と Decorator

Sebastian 氏はコンポーネント拡張の方法として、Higher-Order Component (高階コンポーネント) を gist で提示しています。つまりコンポーネントを引数にとり、新しいコンポーネントを返すコンポーネントです。

次のコードを見て最初は何をやっているのかわからなかったのですが、落ち着いて見れば、ES6 のアロー関数の形式でコンポーネントを引数にとり、Component を継承する無名クラスを戻り値にしています。

export var Enhance = ComposedComponent => class extends Component {
};

より簡潔な表記ができるように Decorator を ES7 で提案されています。すでに Babel は実験的に実装しています。

@Enhance
class MyComponent extends Component {

}

ミックスインは推奨されない

v0.13.0 Beta 1 のリリース記事によれば、ES6 のクラスに標準的なミックスインの方法が存在しないため、ミックスインを使いたいなら、React.createClass を使うようにとのことです。

クラスベースの言語では継承による弊害を避けるためのさまざまな取り組みがあることから、ミックスインを積極的に使ったほうがいいのかと思っていましたが、Redux 開発者の Dan 氏のブログ記事(「Mixins Are Dead. Long Live Composition」)によると、ミックスインの挙動が不透明で、複数のミックスインの衝突を回避するしくみがないので、ミックスインは推奨されないとのことでした。なお、この記事を書いた前日に Dan 氏は Facebook で勤務することを表明しています。

React 開発者の Sebastian 氏の引用されたコメントによると、ミックスインはシステムの制約に対処するための非常手段であり、コンポジションを使いやすくすることの優先順位のほうが高いとのことです。

関数プログラミングの導入は未定

React.js の開発者で Facebook につとめる Sebastian 氏の2015年夏のコメント によると React の開発チームには関数プログラミングのアイディアがあるものの、いつごろ力を素注げばよいのか。オブジェクト指向プログラミングを避けるアイディアがまだないとも言えます (Staltz 氏のコメントより)。

また Sebastian 氏は後述の Cycle.js を賞賛しながらも、Reactive プログラミングのスタイルを採用しなかった理由として、大多数の人が学ぶには大変であることを述べています (Hacker News)。

JSX の代わりに Hyperscript を使う

後述するように、JSX を使うことに疑問視する意見もあり、代替案として、react-hyperscript が挙げられます。

ファイルサイズやアプリの起動時間の比較

モバイル上のJSフレームワークの実行可能性 – ReactとRedux でファイルサイズや ToDo アプリの起動時間の比較が紹介されており、Backbone.js とほぼ同じです。

Flux

とまどい

サーバーサイドの MVC に慣れ親しんでいたので、データの流れを一方向にすることやディスパッチャーを導入することには同意していましたが、アクションに対してとまどいを感じていました。

Good bye Flux, welcome Bacon/Rx?の翻訳」を読むと、イベントストリームを使えば、アクションとストアを分離する必要はないという主張に興味がひかれました。また「Clean Architectureで設計してRxJSを使った話」の取り組みを読み、結果として、Cycle.js の提唱する MODEL-VIEW-INTENT に近い形になったとのことです。

Flux Challenge

Staltz 氏は Flux が複数の非同期データソースのコーディネートをするためのエレガントな方法を提供できるのかどうかを試すために Flux Challenge を公開し、さまざまな回答例を募っています。

Redux

Event Sourcing

Staltz 氏によると、Redux はグローバルな Event Bus と Event Sourcing のためのモダンなフロントエンドの名前とのことです。

Event Sourcing をなぜ使うのかの日本語解説資料として「イベント・ソーシングを知る」(2012年)や「Cutting Edge - 一般的なアプリケーション向けのイベント ソーシング」を挙げることができます。

React なしで Redux

コードの例 が JS BIN で公開されています。

RxJS との連携

Recompose の作者は RxJS との連携にも取り組んでいます (redux-rx)。

コレクション操作ライブラリ

React および RxJS の台頭とともに underscore.js (と互換性のある lodash) に代わる新しいコレクション操作ライブラリが台頭してきています。

Ramda

Why Ramda? によると、先行する Underscore および LoDash と比べて、関数合成や特定のデータの処理に依存しない関数の書き方が提唱されています。

Immutable.js

Immutable.js は Facebook が開発しており、React.js と組み合わせて使う公式チュートリアルもあります (Advanced Performance)。イミュータブルのメリットとして、変更の追跡が容易になることが挙げられます。

RxJS と Immutable.js の連携

irecord は ericelliott 氏によって開発されています。

seamless-immutable

Switching from immutable.js to seamless-immutable の記事によると、Immutable.js を使う場合の課題として、ミュータブルオブジェクトを要素として投入してもミュータブルなままであること、Immutable.js のコレクションと バニラ JS コレクションを何度も変換しなければならないことや API が関数型プログラミングとして非正統的であることが挙げられています。

Freezer

Freezer は Immutable よりもAPI がシンプルでファイルサイズが小さいです。React.js the simple way のチュートリアルでは Flux として使う方法が示されています。

RxJS

Observable のわかりやすい教材

Cycle.js の作者である Staltz 氏は「あなたが求めていたリアクティブプログラミング入門」を公開して大きな注目を集めました。また、「2 minute introduction to Rx」では Observable は「非同期で不変の配列」と見なすことができ、Rx はイベントのための underscore.js (関数型プログラミングのための便利ライブラリ) だと述べています。Twitter では Reactive プログラミングにおいて「X が Y を変える」の代わりに「Y が自分自身を変えるために X を使う」と考えると説明をしています。

ほかに Staltz 氏は「Hot and Cold Observables」を学習資料として挙げています。

RxJS の開発者である Taylor 氏は「An Observable は関数で、起動したときに、現在から終了時のあいだにかけてゼロから無限大の値を返す。」というコメントを残しています。

Taylor 氏は Function、Iterable、Future、Observable を次のように説明しています (ソース)。

1つ ゼロから無限大
Pull Function Iterable
Push Future Observable
  • Function は遅延評価される計算で、起動時において単一の値を同期的に返す。
  • Iterable は遅延評価される計算で、イテレーション時にゼロから無限大の値を同期的に返す。
  • Future は遅延評価される計算で、起動時から未来のある時点まで単一の値を同期的もしくは非同期的に返すもしくは返さない。
  • Observable は遅延評価される計算で、起動時から将来のある時点までゼロから無限大の値を同期もしくは非同期的に返すもしくは返さない。

これらの遅延評価される計算は Producer と Consumer のあいだの関係によってのみ異なります (Consumer が Producer から値を Pull するもしくは Producer が Consumer に値を Push する)。

上記のように定義すれば、Observable は Function、Iterable、Future が可能なことをすべて実現できることがわかります。Taylor 氏は Observable を async なジェネレーター関数として説明することを好むとのことです。

2016年にブレークするかも

Angular 2 は Observable を採用され、Strange Loop のカンファレンスのなかで、RxJS は2016年にブレークするかもしれないという予想が述べられました(Strange LoopでのJavaScriptストリームの紹介)。

チュートリアル

Github でチュートリアルが公開されており、さまざまなコードサンプルが掲載されています。RxJS に用意されている膨大なメソッドを探しやすくするために目的やカテゴリごとに分類されたページがあります。

一通り勉強しておくとよいオペレーター

Staltz 氏が挙げたオペレーターのトップ10は次のとおりです。これらのオペレーターは Which Operator to Use? - Instance Operators から調べることができます。

  • flatMap
  • merge
  • scan
  • combineLatest
  • withLatestFrom
  • map
  • filter
  • just
  • share
  • shareReplay

1つずつ試した練習記録はこちらの記事をご参照ください。

RxJS の代わりに xstream を使う

xstream は RxJS を簡略化したもので、標準のオペレーターの数が厳選されています。くわしくはこちらの記事をご参照ください。

イベントと DOM 操作

最小限読む必要があるのは DOM 操作が記載されている Bridging to EventsHow do I work with jQuery and RxJS でしょう。

jQuery や HTML DOM の RxJS バインディング

RxJS-jQueryRxJS-DOM があります。

Publish/Subscribe パターン

How do I create a custom event emitter? を参照。

リアクティブストリームの見える化

RxVision を参照。

RxJS 以外の選択肢

Bacon.js

著者は2013年に Bacon.js の README.md を翻訳したものの、当時は jQuery のめんどうな使い方にしか見えず、そのまま放置してしまいました。その後、公式サイトのチュートリアルがわかりやすく改善されています。RxJS との比較ドキュメントがあります。

Cycle.js

チュートリアル

公式サイト以外の入門チュートリアル

Plug and Play All Your Observable Streams With Cycle.js が挙げられます。

クリックカウンター

Cycle.js Examples の counter を参照。

ToDoMVC

公式リポジトリの todomvc-cycle を参照。

Ajax

Cycle HTTP Driver (superagent) もしくは Cycle Fetch Driver の公式リポジトリを参照。

localStorage と sessionStorage

Cycle Storage Driver を参照。

アーキテクチャ

RxJS は peer dependency に変更

v5.0.0 で RxJS は Cycle Core にバンドルされなくなり、npm で個別にインストールしなければならなくなりました。

DOM イベントのクエリに CSS セレクタを選ぶ理由

MODEL-VIEW-INTENT に理由が書かれています。開放/閉鎖原則や単一責任の原則を守るためとのことです。intent セレクターを使う場合と使わない場合のコードの比較画像もあります。

Cycle DOM に virtual-dom を採用し、React を使わない理由

Cycle DOM の FAQ によると、Cycle.js 独自のシステムと React のコンポーネントシステムの互換性がないとのことです。virtual-dom はコンポーネントシステムをもたず、React よりもはるかに速いことが挙げられています。また、コンポーネントを中心とした React のベストプラクティスが Cycle.js によってはバッドプラクティスであるために、混乱をもたらすことを避けるためとの述べられています。

staltz 氏が JSX を支持しない理由

To JSX or not to JSX で長い議論が繰り広げられています。
代替案の例として、Elm、Kotlin、Hyperscript-helpers を挙げています。

JSX

<div>
  <img src={user.picture} />
  {user.name}
</div>

Elm

div []
  [ img [ src user.picture ] []
  , text user.name
  ]

Kotlin

div {
  img(src = user.picture)
  +user.name
}

Hyperscript-helpers

div([
  img({src: user.picture}),
  user.name
])

おすすめのインライン CSS ライブラリは?

Staltz 氏は TwitterFree Style を挙げていました。

ルーターの開発ははじまったばかり

Cycle.js で cycle-history の開発がはじめられ、staltz 氏は switch-path を公開しています。ほかに cycle-router5 があります。router5 の作者は FRP に適したルーターの必要性を訴えています (Why router5?)。

Backbone.js と Cycle.js の連携

Backbone.Cycle があります。

Cycle.js 以外の選択肢

Motorcycle.js

Motorcycle.js は Cycle.js の公式サブプロジェクトでパフォーマンスの改善を目的とします。RxJS の代わりに most.js を採用し、標準の DOM ドライバーに Snabbdom を採用しています。

https://t.co/BF9N5PsN8t

Yolk

RxJS と Virtual DOM を組み合わせて使いたいけれど、Cycle.js の勉強時間をじゅうぶんにとれないのであれば、代わりの選択肢として Yolk が挙げられます。Yolk v0.9 で JSX 以外に Hyperscript がサポートされるようになりました。