記事を書くというアイデアがこのスレッドに登場しました。おそらく誰かがそれを読むことに興味を持つでしょう。 筆者(コードを含む)はまあまあだと言う必要がありますが、試してみます。
もちろん、悪魔にうんざりしているチュードリストを作成しますが、デモンストレーションに適したものを見つけるのは困難です。 すぐに動作するアプリケーションへのリンク: zhmyak ( code )。
アプリケーションデータ
そして、すぐに戦闘に入ります。ストレージから始めましょう。 このアプリケーションに必要な唯一のタイプはTodoです。
import { EventEmitter } from 'cellx'; import { observable } from 'cellx-decorators'; export default class Todo extends EventEmitter { @observable text = void 0; @observable done = void 0; constructor(text, done = false) { super(); this.text = text; this.done = done; } }
ここでは、すべてが非常に単純であり、監視対象フィールドが2つあり、1つはタスクのテキストを含み、もう1つはその実装のステータスを示します。
cellx.EventEmitter
からの継承は、将来何らかのフィールドの変更にサブスクライブする必要cellx.EventEmitter
ある場合に必要です。
todo.on('change:text', () => {/* ... */});
このアプリケーションにはこれがなく、継承を削除することができます。何らかの理由で常に事前に作成します。
ルートストアを作成します。
import { EventEmitter, cellx } from 'cellx'; import { observable, computed } from 'cellx-decorators'; import Todo from './types/Todo'; class Store extends EventEmitter { @observable todos = cellx.list([ new Todo('Primum', true), new Todo('Secundo'), new Todo('Tertium') ]); @computed doneTodos = function() { return this.todos.filter(todo => todo.done); }; } export default new Store();
ここでもっと面白いです。 使用されるcellx.list ( new cellx.ObservableList
エイリアス)- new cellx.ObservableList
可能なリストで、 cellx.EventEmitter
を継承し、 change
イベントを生成します。 cellx.EventEmitter
から継承する値として値を受け取るcellx.EventEmitter
フィールドは、そのchange
cellx.EventEmitter
サブスクライブし、このイベントとともに変更されます。 これはすべて、組み込みのコレクションを使用する必要がないことを意味し、 cellx.EventEmitter
からそれらを継承する独自のコレクションを作成できます。 箱から出してcellx.list
とcellx.mapがあります。 別のモジュールは、両方のコレクションのインデックス付きバージョンです: cellx-indexed-collections 。
別の新しいものは計算されたデコレータです。計算フィールドはcellx-aの本質です。計算フィールドの式を書くだけで、追加するときに実行する各todoをサブスクライブする必要はありません。表示されません。リラックスして本質を説明するだけです。 この場合、説明が宣言形式で発生します。イベントやシステム全体に変更がどのように配布されるかを考える必要はなくなり、すべてが一度だけ機能するように記述されます。 さらに、cellxは非常にスマートで、いくつかのトリッキーな最適化を自動的に行います。 依存関係の動的な更新 とイベントの折りたたみと削除では、過剰な計算とインターフェイスの更新が許可されません。 これをすべて手動で行うと、コードは非常に膨大になりますが、さらに悪いことにバグが多くなります。 cellxのデバッグは100年に1回行う必要がありますが、動作するだけです。
アプリケーションビュー
表示レイヤーに移動します。 まず、タスクコンポーネント:
import { observer } from 'cellx-react'; import React from 'react'; import toggleTodo from '../../actions/toggleTodo'; import removeTodo from '../../actions/removeTodo'; @observer export default class TodoView extends React.Component { render() { let todo = this.props.todo; return (<li> <input type="checkbox" checked={ todo.done } onChange={ this.onCbDoneChange.bind(this) } /> <span>{ todo.text }</span> <button onClick={ this.onBtnRemoveClick.bind(this) }>remove</button> </li>); } onCbDoneChange() { toggleTodo(this.props.todo); } onBtnRemoveClick() { removeTodo(this.props.todo); } }
ここに、新しいものからcellx-reactモジュールの observer
デコレータがあります 。 大まかに言うと、単にrender
メソッドを計算セルにし、変更時にReact.Component#forceUpdateを呼び出します。
アプリケーションのルートコンポーネントは残ります。
import { computed } from 'cellx-decorators'; import { observer } from 'cellx-react'; import React from 'react'; import store from '../../store'; import addTodo from '../../actions/addTodo'; import TodoView from '../TodoView'; @observer export default class TodoApp extends React.Component { @computed nextNumber = function() { return store.todos.length + 1; }; @computed leftCount = function() { return store.todos.length - store.doneTodos.length; }; render() { return (<div> <form onSubmit={ this.onNewTodoFormSubmit.bind(this) }> <input ref={ input => this.newTodoInput = input } /> <button type="submit">Add #{ this.nextNumber }</button> </form> <div> All: { store.todos.length }, Done: { store.doneTodos.length }, Left: { this.leftCount } </div> <ul>{ store.todos.map(todo => <TodoView key={ todo.text } todo={ todo } />) }</ul> </div>); } onNewTodoFormSubmit(evt) { evt.preventDefault(); let newTodoInput = this.newTodoInput; addTodo(newTodoInput.value); newTodoInput.value = ''; newTodoInput.focus(); } }
まだいくつかの計算フィールドがあり、計算されるフィールドが現在のインスタンス( this
)にないというStore#doneTodos
のみStore#doneTodos
と異なりますが、cellxはこれに関してこれを制限しません。これらのフィールドは安全ですStore
移動しても、引き続き機能します。 フィールドがその本質のどこにあるべきかを決定する方が良いです-フィールドが特定のコンポーネントに固有である場合、その中で計算され、一般的なストレージで光るのは意味がありません。 この場合、 #leftCount
をリポジトリに転送します。これは他の場所で便利になる可能性があり、 #nextNumber
もここでかなり見栄えがよくなります。
アプリケーションビジネスロジック
Cellxはアクションで使用されないため、可能な限り単純化しました。Fluxではなく、Fluxの観点から何らかのMVCでした。 この単純化をお許しください。
結果
この場合、アプリケーションは非常にシンプルであり、cellxなしでも簡単に記述できます(ここではサブスクリプションは不要です)。アプリケーション内のリンクがさらに複雑になると、cellx-eの記述の複雑さは線形に増加します。いや、ある時点で、パイントなしではそれを把握できないイベントのミッシュマッシュになります。 問題を解決するために、リアクティブプログラミングに加えて、長所と短所で他のアプローチがありますが、それらを比較することは別の話です(簡単に言えば、少なくとも不必要な計算が多く、結果としてパフォーマンスが低下するために失われます)。
一般に、コードはすべてであり、もう一度結果へのリンクです: zhmyak ( code )。
他のライブラリとの比較
モブックス
ほとんどの場合、彼らはMobXとの違いを求めます。 これは最も近いアナログであり、いくつかの違いがあります。
- cellxは約10倍高速です。
- アトムに関する記事では、セルがもう少しできるようにするput / pullメソッド/オプションを探しました: 同期ストレージとの値の同期、非同期ストレージとの値の同期 、 pullについて 。 MobXに似たものは見つかりませんでした。
- cellbとは異なり、MobXはReactとより良く統合されており、アプリケーションのビジネスロジックレイヤーに何らかの形で組み込まれています。 私はまだ彼がそこにいた理由を理解していませんでしたが、どういうわけか私はそれを必要としていました。
Kefir.js、Bacon.js
ここで、違いはより重要です。 速度の遅れはさらに大きくなりますが、これは重要ではありません。 これらのライブラリは、おそらくより機能的な方法で、わずかに異なる方法で計算されたセルを作成することをお勧めします。 cellx-eでは次のようになります。
var val2 = cellx(() => val() + 1);
これらのライブラリで、次のようなものに変更します(擬似コード、私は正確に覚えていないので、ポイントではありません):
var val2 = val.lift(add(1));
さらに、より美しく、人間が読み取れるコードで、著しく大きい入力しきい値でマイナスです。これは、すべての場面で100500メソッドを覚えておく必要があるためです(もちろん、最小限のセットで行うことができます)。
同時に、cellxでは、独自のメソッドをセルに追加する機会があり、これらのライブラリのレベルにそれをもたらすことを妨げるものは何もありません。それは低レベルであると言えます。
地下室
ライブラリに関する質問と、そのさらなる開発のアイデアはgithubで受け付けています。
ご清聴ありがとうございました。