先週、私は 双方向データバインディング(Angular) と 一方向データフロー(React/Flux) の違いを理解しようと努めてきました。彼らは、一方向のデータフローはより強力で理解しやすく従うと述べています:これは決定論的であり、副作用の回避に役立ちます。しかし、私の初心者の目では、どちらもほぼ同じように見えます。ビューはモデルをリッスンし、モデルはビューに対して行われたアクションに反応します。どちらも、モデルは真の単一のソースであると主張しています。
本当にがどのように異なり、一方向のデータフローがより有益であり、推論しやすいのかを理解できる方法で誰かが包括的に説明できますか?
Angularには多くのコントローラーがあります。1つの例は、ユーザーがコントローラー1によって管理されるビュー1でアクションをトリガーすることです。コントローラー1は何かを実行しますが、別のコントローラーによってキャッチされたイベントも発生させます2.コントローラー2が$ scopeの一部のプロパティを更新し、ビュー2が突然変更されます。
突然、ビュー1の操作が更新され、ビュー2が更新されました。いくつかの非同期コールバックともう少し多くのイベントチェーンをスローすると、ビューが更新されているとき/方法が正確にわかりません。
Flux/Reduxを使用すると、データフローが一方向になります。ビューがモデルを更新することは決してなく、ビューはアクション(更新の意図)をディスパッチすることしかできませんが、ストア/リデューサーは更新の処理方法を決定できます。各ビューで実行できるアクションを簡単に確認できるため、データフローについてより簡単に推論できます。次に、そのアクションがストアによってどのように処理されているかを確認し、何を更新できるかを正確に知ることができます。
これは、ビューとモデルのいずれかが変更されるたびに同期するメカニズムによって可能になりました。 Angularでは、変数を更新すると、その変更検出メカニズムがビューの更新を処理し、逆もまた同様です。どうしたの?変更検出メカニズムを制御しません。ビューを強制的に更新するには、ChangeDetectorRef.detectChangesまたはNgZone.runに頼らざるを得なかった。
Angularの変更検出に深く入り込まないようにするには、変数を変更したとき、またはオブザーバブルが解決された後に変更されたときに、必要なものを更新することが信頼できますが、実行方法とタイミングがわからないことに気づくでしょう、また、変数が変更された後にビューが更新されない場合もあります。言うまでもないことですが、問題が発生した場所とタイミングを見つけるのが面倒な場合もあります。
つまり、ビューは常にモデルから状態を取得します。ビューを更新するには、まずモデルを更新してから、ビューを再描画する必要があります。 Reactは、実際のDOMではなく、メモリ上に保持する仮想DOMを比較するため、ビューの再描画プロセスを非常に効率的にします。しかし、この動的で変更検出はどのように機能しますか?手動でトリガーします。
Reactでは、状態の新しい値を設定します。これにより、ReactDOM.renderが発生し、DOMの比較/更新プロセスが発生します。 React/Reduxでは、ストア(単一の信頼できるソース)を更新するアクションをディスパッチしてから、残りのアクションをディスパッチします。重要なのは、いつ何が変更され、何が変更の原因になったかを常に知っているということです。これにより、問題解決が非常に簡単になります。アプリが状態に依存している場合は、変更をトリガーしたアクションの前後でそれを確認し、変数が想定どおりの値を持っていることを確認します。
プラットフォームに依存しない観点からは、それほど違いはありません。一方向のフローと双方向のバインディングを分けるのは、変更時の変数の更新です。したがって、概念的にはそれほど離れていないという印象は、実際の使用からは離れていません。
アプリが単なるウィザードフローであるとしましょう。ただし、いくつかの複雑な相互作用があります。つまり、1つのステップmight次のステップの動作を変更します。
アプリは問題なく実行されていますが、ある日、ユーザーがトリッキーな手順の1つでバグを報告しています。
双方向バインディングと一方向バインディングでデバッグはどのように機能しますか?
動作の違いを確認し始め、運が良ければ、ユーザーと同じポイントに到達してバグを特定します。しかし、同時にアプリのさまざまな部分の間に奇妙な相互作用があるかもしれません。正しくないデータバインディング(たとえば、モデルの状態を複製するがバインディングではない)や、デバッグが難しいコンポーネント間の他の奇妙な複雑さがあるかもしれません。 分離バグを見つけるのは難しいかもしれません。
state
オブジェクトを取得するだけです。現在、大きなjavascriptオブジェクトにアプリのすべての情報が含まれています。開発環境で同じ状態をロードすると、大きな可能性がありますアプリはまったく同じように動作します。特定の状態の回帰テストを記述して、発生している問題を正確に特定することもできます。
一言で言えば、一方向バインディングはそれを可能にします非常に簡単に複雑なアプリをデバッグする。多くのことを行う必要はなく、ユーザーの現在の状態をコピーします。
それでもうまくいかない場合でも、アクションをログに記録できます。たとえば、Angularですべての状態変更アクションを追跡する簡単な方法はAFAIRではありません。 Reduxではかなり、かなり簡単です。
データフローここにwriteイベントのフローがあります-状態の更新
これらのイベントは、ビューとコントローラー(およびHTTPバックエンドなどのサービス)の間を流れています
一方向の流れは基本的に巨大なサイクルです:
双方向フロー別名データバインディングは2つの状態をバインドします:ほとんどの場合、1つはコントローラー内部にあります(例:変数) 、およびビュー内の1つ(テキストボックスのコンテンツなど)。 Bindingは、1つのピースが変更されると、もう1つのピースも変更されて同じ値を取得することを意味します。したがって、関係する状態のピースは1つだけであるように見せかけることができます(実際には2つあります) )。 Writeイベントはコントローラーとビューの間を行き来しています-したがってtwo-way。
この特定のテキストボックスのコンテンツを保持している変数を把握する必要がある場合、データバインディングは便利です。しかし、実際には2つの部分がある1つの状態の幻想を維持するには、複雑なフレームワークが必要です。通常、フレームワーク固有の構文を使用してビューのコードを記述する必要があります。 e。さらに別の言語を学ぶ。
一方向のデータフローは、その追加のエンティティ(イベントフロー)を活用できる場合に最適です。そして、通常、次のことができます-元に戻す/やり直し、ユーザーアクションのリプレイ(デバッグなど)、レプリケーションなどに役立ちます。これをサポートするコードははるかに簡単で、通常は代わりにプレーンJavaScriptで記述できますフレームワーク固有の構文。一方、データバインディングがなくなったため、定型文を節約できなくなりました。
また、この回答の視覚的な説明もご覧ください https://stackoverflow.com/a/37566693/1643115 。一方向の矢印と両方向の矢印は、それぞれ一方向と双方向のデータフローを視覚的に表します。