双向数据绑定(Angular)与单向数据流(React/Flux)

Gle*_*mad 21 javascript flux angularjs reactjs reactjs-flux

在上周,我一直试图理解双向数据绑定(Angular)单向数据流(React/Flux)是如何不同的.他们说单向数据流更强大,更容易理解和遵循:它是确定性的,有助于避免副作用.然而,在我的新手眼中,它们看起来几乎相同:视图监听模型,模型对视图所做的操作做出反应.两者都声称模型是真实唯一来源.

任何人都能以可理解的方式全面解释它们之间的真正差异以及单向数据流如何更有益且更容易推理?

Cuz*_*zox 15

Angular 的双向数据绑定

它是通过一种机制使视图和模型在任何一个更改时同步。在 Angular 中,你更新一个变量,它的变化检测机制将负责更新视图,反之亦然。有什么问题?您无法控制变更检测机制。我发现自己不得不求助于 ChangeDetectorRef.detectChanges 或 NgZone.run 来强制更新视图。

为了不深入研究 Angular 中的变化检测,您相信它会在您更改变量时更新您需要的内容,或者在 observable 解析后更改时,但您会发现您不知道它如何以及何时运行,有时它不会在变量更改后更新您的视图。毋庸置疑,有时很难找到问题发生的地点和时间。

React 的单向数据流

这意味着视图总是从模型中获取它的状态。要更新视图,需要先更新模型,然后重新绘制视图。React 使视图重绘过程非常高效,因为它比较的不是实际的 DOM,而是它保存在内存中的虚拟 DOM。但是变化检测在这种动态中是如何工作的呢?好吧,您手动触发它。

在 React 中,您设置状态的新值,然后导致 ReactDOM.render,从而导致 DOM 比较/更新过程。在 React/Redux 中,您调度更新存储(单一事实来源)的操作,然后是其他操作。重点是,你总是知道什么时候东西发生了变化,以及是什么导致了变化。这使得解决问题非常简单。如果您的应用程序依赖于状态,则在触发更改的操作之前和之后查看它,并确保变量具有它们应该具有的值。

除了实现

从独立于平台的角度来看,它们并没有太大的不同。将单向流与双向绑定分开的是对变化的可变更新。因此,您认为它们在概念上彼此相距不太远的印象并没有脱离它们的实际用途。


lua*_*ped 9

在Angular中你有很多控制器.一个示例是用户触发由控制器1管理的视图1上的操作.控制器1执行某些操作但也触发由另一个控制器2捕获的事件.控制器2更新$ scope上的某些属性,而视图2突然显示改变.

突然对View 1进行操作,更新View 2.如果我们现在抛出一些Async回调和更多的事件链,您可能不再确切知道您的视图何时/如何更新.

使用Flux/Redux,您可以使用单向数据流.视图永远不会更新模型,视图只能调度操作(更新意图),但让store/reducer决定如何处理更新.您可以更轻松地推断数据流,因为您可以轻松查看每个视图可以触发的操作.然后跟进以查看商店如何处理该操作,您可以确切地知道可以更新的内容.

  • 在上面的Angular示例中,它与React/Flux中的说法不同:Store 2在View 1触发的操作上向调度程序注册回调,然后触发事件到View 2进行更新?它仍然是对View 1更新View 2的操作,在我看来,与我在Angular中实现它时相比,React/Flux中的这个实现并没有提供额外的价值.不确定"异步回调和更多事件链"将如何改变游戏.你能详细说明一下吗? (2认同)
  • @GlennMohammad 使用 Flux(或 Redux),您可以跟踪任何意图。变异的每一个意图都遍历Dispatcher;这样您就可以毫不费力地了解应用程序中的任何操作。使用 Angular 示例,您将面对 **隐式** 意图/动作,而对其传输没有任何限制。这将很难推理。 (2认同)

Kir*_*kov 6

  1. 这里的数据流事件流——即状态更新

  2. 这些事件在视图和控制器(以及服务,例如 HTTP 后端)之间流动

  3. 单向流基本上就是巨循环:

    • 应用视图使用(读取,而不是写入)应用状态来呈现
    • 当应用程序从外部获得一些刺激时(用户在输入字段中输入一些文本,或者 HTTP 请求的结果已经到达),它会发出事件 - 或者,在 Redux/Flux 俚语中,分派一个动作
    • 来自所有控制器和视图的所有事件都流入单个接收器 - 调度函数(reducer);尽管调度函数的性质允许它由更简单的调度函数组成,但从概念上讲,整个应用程序只有一个调度程序
    • 调度程序使用事件来确定要更新状态的哪一部分
    • 开始
  4. 双向流又名数据绑定绑定两个状态:在大多数情况下,一个在控制器内部(例如某个变量),另一个在视图内部(例如文本框的内容)。绑定意味着,当一块发生变化时,另一块也会发生变化并获得相同的值,因此您可以假装只涉及一个状态(而实际上有两个)。写入事件在控制器和视图之间来回传递——因此是双向的

  5. 当您需要找出保存此特定文本框内容的变量时,数据绑定很酷 - 它会立即显示。但是它需要复杂的框架来维持一个状态的错觉,而实际上有两个状态。通常,您将被迫使用特定于框架的语法来编写视图代码——即学习另一种语言。

  6. 当您可以利用额外的实体 - 事件流时,单向数据流很酷。而且,通常,您可以 - 它对撤消/重做、用户操作重放(例如用于调试)、复制等很有用。支持这一点的代码要简单得多,通常可以用纯 JavaScript 编写特定于框架的语法。另一方面,由于您不再有数据绑定,它不再为您节省一些样板。

另外,在这个答案中看到很好的视觉解释:https : //stackoverflow.com/a/37566693/1643115。单头和双头箭头分别在视觉上表示单向和双向数据流。


Ped*_*nto 5

假设您的应用程序只是一个向导流程,但它有一些复杂的交互,即一个步骤可能会改变后续步骤的行为。

您的应用运行良好,但有一天用户报告了其中一个棘手步骤的错误。

调试如何处理双向绑定和单向绑定?

双向绑定

我会开始检查哪些行为是不同的,如果运气好的话,可以与用户达到相同的目的并查明错误。但与此同时,应用程序的不同部分之间可能会有一些奇怪的交互。我可能有一些不正确的数据绑定(例如复制模型状态但没有绑定)或其他难以调试的组件之间的奇怪复杂性。可能很难隔离错误。

单向绑定

你只需抓住state对象。它在一个大的 javascript 对象中包含当前应用程序的所有信息。您在开发环境中加载相同的状态,您的应用程序很可能会表现得完全相同。您甚至可以使用给定的回归状态编写测试并查明正在发生的确切问题。

结论

简而言之,单向绑定使调试复杂的应用程序变得非常容易。您不必做太多然后复制用户的当前状态。

即使这不起作用,您也可以记录操作。例如,没有一种简单的方法可以跟踪Angular上的所有状态修改操作。使用 Redux,它非常非常简单