Cycle.js 公式サイトのトップページの翻訳です。ライセンスは原文と同じ MIT とします。
アプリと外の世界をサーキットとして考える
Cycle の抽象化の核心はアプリケーションを純粋な関数である main()
とすることです。入力は外の世界からの Observable な副作用として扱い、出力 (sink) は外の世界に影響を及ぼすコマンドもしくは更新です。外の世界の副作用はドライバーによって管理されます。ドライバーは DOM 効果、HTTP 効果などを扱うプラグインです。
main()
の内部は Reactive プログラミングをもとに開発されており、関心の分離を最大化し、コードを組織化するためのクリーンで十分に宣言的な方法を提供します。データフローは平易で見やすく、コードを理解しやすくします。
コードの例
npm install rx @cycle/core @cycle/dom
import Cycle from '@cycle/core';
import {div, label, input, h1, makeDOMDriver} from '@cycle/dom';
function main(sources) {
const sinks = {
DOM: sources.DOM.select('.field').events('input')
.map(ev => ev.target.value)
.startWith('')
.map(name =>
div([
label('Name:'),
input('.field', {attributes: {type: 'text'}}),
h1('Hello ' + name)
])
)
};
return sinks;
}
Cycle.run(main, { DOM: makeDOMDriver('#app-container') });
Functional で Reactive
Functional は「クリーン」であること、Reactive は「分離されている」ことを意味します。Cycle.js のアプリは純粋な関数から構成され、このことは副作用が伴わず、入力と生成される出力だけを知っていればよいことを意味します。基本要素は Observable で RxJS からつくられます。RxJS は Reactive プログラミングライブラリで、イベント、非同期、エラーに関連するコードをシンプルなものにします。RxJS でアプリケーションを構造化することは関心の分離を意味します。データの一部を動的に更新することはすべて co-located であり、外部から変更することは不可能です。結果として、Cycle のアプリには全体的に this
がなく、setState()
もしくは foo.update()
のような手続き型の呼び出しに類するものがありません。
シンプルで簡潔
Cycle.js は学ぶコンセプトがとても少ないフレームワークです。コア API はたった1つの関数: run(app, drivers)
です。それに加えて、 Observable 、 関数、 ドライバー (異なる種類の副作用のためのプラグイン)、とスコープつきのコンポーネントを隔離するためのヘルパー関数があります。これが「魔法」がとても少ないフレームワークです。たいていの基本要素はたんなる JavaScipt 関数です。通常、「魔法」がないと、とても冗長なコードになりがちですが、RxJS Observable のおかげで、複雑なデータフローを少しのオペレーションで構築することができるので、Cycle.js のアプリが小さく読みやすいものであることがわかります。
拡張とテストのしやすさ
ドライバーはプラグインのような関数でシンク (sink) からメッセージを受け取り、手続き型の関数を呼び出します。すべての副作用はドライバーに含まれます。このことはアプリケーションがたんなる純粋な関数であることを意味し、ドライバーを交換しやすくなります。コミュニティは React Native、HTML5 Notification、Socket.io などを開発しています。ソース (Source) とシンク (sink) はそれぞれ Adapter と Port として使うことができます。このことは、テストの大半が入力の提供と出力の検査であることも意味します。深いモックは必要ありません。アプリケーションはたんなるデータの純粋な変換です。
明確なデータフロー
Cycle.js のアプリにおいて、それぞれの Observable の宣言はデータフローグラフのノードで、複数の宣言のあいだの依存関係は矢印であらわされます。このことはアプリのコードと外部の入出力のあいだに関するミニマップのようなデータフローのグラフが一対一の関係があることを意味します。
多くのフレームワークでは、データの流れは暗黙のなかにあります。アプリのなかでのデータの移動がどのようになっているのかメンタルモデルを築き上げる必要があります。Cycle.js では、データの流れは自分が書いたコードを読むことで明確です。
合成可能である
Cycle.js にはコンポーネントが用意されており、ほかのフレームワークと異なり、単独の Cycle.js アプリは、どんなに複雑であれば、関数であり、より大きな Cycle.js アプリで再利用できます。
ソース (source) とシンク (sink) はアプリケーションとドライバーのあいだのインターフェイスであるとともに、子コンポーネントと親コンポーネントのあいだのインターフェイスでもあります。Cycle.js コンポーネントはほかのフレームワークと似たようなシンプルな GUI ウィジェットですが、GUI に限定されません。Web Audio コンポーネント、ネットワークリクエストコンポーネントなどをつくることができます。ソース/シンクは DOM に限定されるインターフェイスではないからです。