为什么通过 Facebook Flux 使用 Redux?

我已经阅读了这个答案减少了样板 ,看了几个 GitHub 示例,甚至尝试了一点 redux(待办事项应用程序)。

据我了解, 官方的 redux doc 动机与传统的 MVC 架构相比具有优势。但是它没有提供以下问题的答案:

为什么要通过 Facebook Flux 使用 Redux?

这仅仅是编程风格的一个问题:功能性还是非功能性?还是问题出在 redux 方法之后的 abilities / dev-tools 中?也许缩放?还是测试?

如果我说 redux 对于来自函数式语言的人来说是一种变化,那我是对的吗?

为了回答这个问题,您可以比较实现 Redux 在通量和 Redux 上的动机点的复杂性。

以下是来自官方 redux doc 动机的动机点:

  1. 处理乐观更新( 据我所知,这几乎不依赖于第 5 点。很难在 Facebook 流量中实现它吗?
  2. 在服务器上渲染( facebook 流量也可以做到这一点。与 redux 相比有什么好处吗?
  3. 在执行路线转换之前获取数据( 为什么不能在 facebook 流量中实现它?有什么好处?
  4. 热重载( React Hot Reload可能实现。为什么需要 redux?
  5. 撤消 / 重做功能
  6. 还有其他要点吗?像坚持状态...

答案

Redux 作者在这里!

Redux 与 Flux 没有什么不同。总体而言,它具有相同的架构,但是 Redux 可以通过使用 Flux 使用回调注册的功能组合来减少一些复杂性。

Redux 并没有根本的区别,但是我发现它使某些抽象更容易实现,或者至少可以实现,这在 Flux 中很难实现或不可能实现。

减速机组成

例如,分页。我的Flux + React Router 示例处理分页,但是该代码很糟糕。令人恐惧的原因之一是Flux 使跨商店重用功能变得不自然。如果两个存储需要处理分页以响应不同的操作,则它们要么需要从一个通用的基本存储中继承(糟糕!当您使用继承时,您将自己锁定在特定的设计中),或者从内部存储中调用外部定义的函数。事件处理程序,需要以某种方式在 Flux 商店的私有状态下进行操作。整个过程都是混乱的(尽管绝对是可能的)。

另一方面,由于 Redux 的组成,Redux 分页很自然。减速器一直都在运行,因此您可以编写一个减速器工厂来生成分页减速器 ,然后在减速器树中使用它 。之所以如此简单,关键是因为在 Flux 中存储是平坦的,但是在 Redux 中,reduce 可以通过功能组合来嵌套,就像 React 组件可以嵌套一样。

此模式还启用了诸如无用户代码undo / redo 之类的出色功能。 您能想象两行代码将撤消 / 重做插入 Flux 应用程序吗?几乎不。再次使用 Redux,这归功于 reducer 的组成模式。我需要强调一点,这没有什么新鲜的 - 这是Elm Architecture 所开创和详细描述的模式,它本身受 Flux 的影响。

服务器渲染

人们一直在使用 Flux 在服务器上进行良好的渲染,但是看到我们有 20 个 Flux 库,每个库都试图使服务器渲染 “更容易”,也许 Flux 在服务器上有一些粗糙的边缘。事实是,Facebook 并没有做太多的服务器渲染,因此他们并不是很在意它,而是依靠生态系统来简化它。

在传统的 Flux 中,商店是单身人士。这意味着很难为服务器上的不同请求分离数据。不是没有,但是很难。这就是为什么大多数 Flux 库(以及新的Flux Utils )现在建议您使用类而不是单例的原因,因此您可以根据请求实例化存储。

在 Flux 中,仍然需要解决以下问题(您自己或在您喜欢的 Flux 库(例如FlummoxAlt )的帮助下):

  • 如果商店是类,那么如何根据请求使用分派器创建和销毁它们?我什么时候注册商店?
  • 如何合并来自商店的数据,然后在客户端重新补充数据?为此,我需要实现特殊方法吗?

诚然,Flux 框架(不是普通的 Flux)可以解决这些问题,但我发现它们过于复杂。例如, Flummox 要求您在商店中实现serialize()和 deserialize deserialize() 。 Alt 通过提供takeSnapshot()来自动解决此问题,该takeSnapshot()会自动在 JSON 树中序列化您的状态。

Redux 走得更远: 由于只有一个商店(由许多 reducers 管理),因此您不需要任何特殊的 API 来管理(重新)水化。您无需 “冲洗” 或 “水化” 商店 - 只需一个商店,您就可以读取其当前状态,或创建具有新状态的新商店。每个请求都会获得一个单独的商店实例。 了解有关使用 Redux 进行服务器渲染的更多信息。

同样,在 Flux 和 Redux 中都有可能发生这种情况,但是 Flux 库通过引入大量 API 和约定来解决此问题,而 Redux 甚至不必解决它,因为在由于概念上的简单性而获得第一名

开发者经验

实际上,我并没有打算让 Redux 成为流行的 Flux 库 - 我是在我的ReactEurope 演讲中这篇文章的,当时我正在做时光热装 。我的一个主要目标是: 可以即时更改减速器代码,甚至可以通过取消操作来 “更改过去”,并查看状态是否正在重新计算。

我还没有看到能够执行此操作的单个 Flux 库。 React Hot Loader 也不允许您这样做 - 实际上,如果您编辑 Flux 商店,它会中断,因为它不知道如何处理它们。

当 Redux 需要重新加载化replaceReducer()器代码时,它将调用replaceReducer() ,并且应用程序将使用新代码运行。在 Flux 中,数据和功能在 Flux 存储中纠缠在一起,因此您不能 “仅替换功能”。此外,您还必须以某种方式向 Dispatcher 重新注册新版本,而 Redux 甚至没有。

生态系统

Redux 具有丰富且快速增长的生态系统 。这是因为它提供了一些扩展点,例如中间件 。它的设计考虑到了用例,例如日志记录 ,对Promises 的支持, Observables路由不变性开发检查持久性等。并非所有这些工具都将有用,但是很高兴能够使用一组可以轻松组合在一起一起工作的工具。

简单

Redux 保留了 Flux 的所有好处(动作的记录和重放,单向数据流,相关的变异),并增加了新的好处(轻松的撤消重做,热重装),而无需引入 Dispatcher 和商店注册。

保持简单很重要,因为在实现更高级别的抽象时,它可以使您保持理智。

与大多数 Flux 库不同,Redux API 表面很小。如果您删除了开发人员警告,评论和健全性检查,那么这是99 行 。没有棘手的异步代码可调试。

您实际上可以阅读它并了解 Redux 的全部内容。


另请参阅我对使用 Redux 与 Flux 相比的缺点的回答

在 Quora 中,有人说

首先,完全可以使用不带 Flux 的 React 编写应用程序。

我创建的这个可视化图表也显示了两者的快速视图,对于不想阅读完整说明的人们来说,可能是一个快速答案: 通量vs Redux

但是,如果您仍然想了解更多信息,请继续阅读。

我相信您应该从纯 React 开始,然后学习 Redux 和 Flux。在具有 React 的实际经验之后,您将看到 Redux 是否对您有帮助。

也许您会觉得 Redux 恰好适合您的应用程序,也许您会发现 Redux 试图解决您并未真正遇到的问题。

如果直接从 Redux 开始,则可能会得到过度设计的代码,与没有 Redux 相比,代码更难维护且存在更多错误。

来自Redux docs

动机
随着对 JavaScript 单页应用程序的需求变得越来越复杂,我们的代码必须管理比以往更多的状态。此状态可以包括服务器响应和缓存的数据,以及尚未持久保存到服务器的本地创建的数据。 UI 状态的复杂性也在增加,因为我们需要管理活动路径,选定的选项卡,微调框,分页控件等。

管理这种不断变化的状态非常困难。如果一个模型可以更新另一个模型,那么一个视图可以更新一个模型,该模型可以更新另一个模型,这又可能导致另一个视图更新。在某些时候,您不再了解应用程序中发生的情况,因为您无法控制其状态的时间,原因和方式。当系统是不透明且不确定的系统时,很难重现错误或添加新功能。

好像还不够糟糕,请考虑一下新要求在前端产品开发中变得很普遍。作为开发人员,我们有望处理乐观更新,服务器端渲染,在执行路由转换之前获取数据等。我们发现自己试图管理一种前所未有的复杂性,并且不可避免地会问一个问题:是时候该放弃了吗?答案是不。

这种复杂性很难处理,因为我们混用了两个人类难以理解的概念:突变和异步性。我称他们为 Mentos 和可乐。两者分开时可能很棒,但在一起会造成混乱。像 React 这样的库试图通过消除异步和直接 DOM 操作来解决视图层中的这一问题。但是,管理数据状态由您自己决定。这就是 Redux 的用武之地。

遵循 Flux,CQRS 和 Event Sourcing 的脚步,Redux 试图通过对更新的方式和时间施加一定的限制来使状态突变可预测。这些限制反映在 Redux 的三个原则中。

同样来自Redux docs

核心概念
Redux 本身非常简单。

假设您的应用状态被描述为一个普通对象。例如,待办事项应用程序的状态可能如下所示:

{
  todos: [{
    text: 'Eat food',
    completed: true
  }, {
    text: 'Exercise',
    completed: false
  }],
  visibilityFilter: 'SHOW_COMPLETED'
}

该对象就像一个 “模型”,只是没有设置器。这样一来,代码的不同部分就无法任意更改状态,从而导致难以重现的错误。

要更改状态中的某些内容,您需要调度一个动作。动作是描述所发生情况的普通 JavaScript 对象(注意我们如何引入任何魔术?)。以下是一些示例操作:

{ type: 'ADD_TODO', text: 'Go to swimming pool' }
{ type: 'TOGGLE_TODO', index: 1 }
{ type: 'SET_VISIBILITY_FILTER', filter: 'SHOW_ALL' }

通过将每项更改都描述为一项操作,我们可以清楚地了解应用程序中正在发生的事情。如果有什么变化,我们知道为什么会变化。动作就像发生了什么的面包屑。最后,为了将状态和动作联系在一起,我们编写了一个称为 reducer 的函数。同样,这没什么神奇的 - 它只是一个将状态和操作作为参数并返回应用程序的下一个状态的函数。对于大型应用程序很难编写这样的功能,因此我们编写了一些较小的功能来管理部分状态:

function visibilityFilter(state = 'SHOW_ALL', action) {
  if (action.type === 'SET_VISIBILITY_FILTER') {
    return action.filter;
  } else {
    return state;
  }
}

function todos(state = [], action) {
  switch (action.type) {
  case 'ADD_TODO':
    return state.concat([{ text: action.text, completed: false }]);
  case 'TOGGLE_TODO':
    return state.map((todo, index) =>
      action.index === index ?
        { text: todo.text, completed: !todo.completed } :
        todo
   )
  default:
    return state;
  }
}

然后,我们编写另一个 reducer,通过调用这两个 reducer 来对应的状态键来管理应用程序的完整状态:

function todoApp(state = {}, action) {
  return {
    todos: todos(state.todos, action),
    visibilityFilter: visibilityFilter(state.visibilityFilter, action)
  };
}

这基本上是 Redux 的整体思想。请注意,我们尚未使用任何 Redux API。它附带了一些实用程序来简化此模式,但是主要思想是您描述如何随着时间的推移响应操作对象来更新状态,并且您编写的代码 90%只是纯 JavaScript,而没有使用 Redux 本身,其 API 或任何魔术。

最好从阅读 Dan Abramov 的这篇文章开始,他在写 Redux 时讨论 Flux 的各种实现及其折衷: Flux Frameworks 的演变。

其次,您链接到的动机页面并没有真正讨论 Redux 的动机,而是讨论 Flux(和 React)背后的动机。尽管仍然没有解决与标准 Flux 架构在实现上的差异,但 “ 三项原则”是 Redux 所特有的。

基本上,Flux 具有多个存储,这些存储可响应于与组件的 UI / API 交互来计算状态更改,并将这些更改作为组件可以订阅的事件进行广播。在 Redux 中,每个组件都只能订阅一个商店。 IMO 至少感觉到 Redux 通过统一(或减少,如 Redux 所说的)流回到组件的数据流来进一步简化和统一数据流 - 而 Flux 则集中于统一数据流的另一侧 - 模型。

我是早期采用者,并使用 Facebook Flux 库实现了中型单页应用程序。

我在谈话中有些晚了,我只想指出,尽管我最大的希望是,Facebook 似乎将 Flux 的实现视为概念的证明,并且从未受到应有的重视。

我鼓励您使用它,因为它展示了 Flux 架构的更多内部工作,这是很有教益的,但是同时,它没有像 Redux 这样的库提供许多好处(这些没有对于小型项目来说很重要,但对于大型项目则变得非常有价值)。

我们已经决定继续前进,我们将转移到 Redux,我建议您这样做;)

这是 Redux over Flux 的简单说明。 Redux 没有调度程序,它依赖于称为 reducers 的纯函数。它不需要调度程序。每个动作由一个或多个 reduce 处理,以更新单个存储。由于数据是不可变的,reduces 返回一个新的更新状态,该状态更新了商店在此处输入图片说明

有关更多信息, Flux vs Redux

我在 Flux 工作了很长时间,现在在 Redux 中工作了很长时间。正如 Dan 所指出的,两种架构都没有太大不同。事实是 Redux 使事情变得更简单,更清洁。它教会了您有关 Flux 的一些知识。例如,Flux 是单向数据流的完美示例。关注点分离在我们拥有数据的地方,其操作和视图层是分离的。在 Redux 中,我们有相同的东西,但我们还了解了不变性和纯函数。

来自一个新的 react / redux 采用者,它是在 2018 年中期从 ExtJS(几年)迁移而来的:

向后滑下 redux 学习曲线后,我遇到了相同的问题,并认为纯通量像 OP 一样简单。

我很快看到了 redux 相对于通量的好处,如上面的答案所述,并且正在将其用于我的第一个应用程序。

在再次掌握样板时,我尝试了一些其他状态管理库,发现的最好是rematch

比普通的 redux 更直观,它减少了 90%的样板,减少了我在 redux 上花费的时间的 75%(我认为是图书馆应该做的事情),我能够得到一些企业应用程序马上去。

它还使用相同的 redux 工具运行。这是一篇很好的文章 ,涵盖了一些好处。

因此,对于到达此 SO 职位搜索 “更简单的 redux” 的任何人,我建议您尝试一下它作为 redux 的一种简单替代方法,它具有所有优势和 1/4 的样板。

根据这篇文章: https : //medium.freecodecamp.org/a-realworld-comparison-of-front-end-frameworks-with-benchmarks-2019-update-4be0d3c78075

您最好使用 MobX 来管理应用程序中的数据以获得更好的性能,而不是 Redux。