为什么在 Facebook Flux 上使用 Redux?

IT技术 javascript reactjs reactjs-flux flux redux
2021-01-25 10:34:54

我已经阅读了这个答案减少了样板文件,查看了一些 GitHub 示例,甚至尝试了一些 redux(todo 应用程序)。

据我了解,与传统 MVC 架构相比官方 redux 文档动机提供了优点。但它没有提供问题的答案:

为什么你应该在 Facebook Flux 上使用 Redux?

这只是编程风格的问题:函数式还是非函数式?或者问题在于 redux 方法遵循的能力/开发工具?也许缩放?还是测试?

如果我说 redux 是来自函数式语言的人的助推器,我说得对吗?

要回答这个问题,您可以比较实现 redux 在flux 与 redux 上的动机点的复杂性。

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

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

Redux 作者在这里!

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

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

减速机组成

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

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

这种模式还支持无用户代码undo/redo等精彩功能您能想象将 Undo/Redo 插入 Flux 应用程序是两行代码吗?几乎不。使用 Redux,再次感谢 reducer 组合模式。我需要强调的是,它并没有什么新鲜之处——这是Elm Architecture 中开创和详细描述的模式,它本身受 Flux 的影响。

服务器渲染

人们一直在使用 Flux 在服务器上进行渲染,但是看到我们有 20 个 Flux 库,每个库都试图使服务器渲染“更容易”,也许 Flux 在服务器上有一些粗糙的边缘。事实上,Facebook 并没有做太多的服务器渲染,所以他们并没有很关心它,并依靠生态系统来让它变得更容易。

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

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

  • 如果商店是类,我如何根据请求使用调度程序创建和销毁它们?我什么时候注册商店?
  • 我如何从存储中提取数据,然后在客户端上重新提取数据?我需要为此实施特殊方法吗?

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

Redux 更进一步:由于只有一个 store(由许多 reducer 管理),因此您不需要任何特殊的 API 来管理(重新)水化。你不需要“刷新”或“水合”存储——只有一个存储,你可以读取它的当前状态,或者用一个新的状态创建一个新的存储。每个请求都会获得一个单独的商店实例。阅读有关使用 Redux 进行服务器渲染的更多信息。

同样,这是在 Flux 和 Redux 中都可能出现的情况,但是 Flux 库通过引入大量 API 和约定来解决这个问题,而 Redux 甚至不必解决它,因为它在首先要感谢概念上的简单性。

开发者体验

我实际上并不打算将 Redux 变成一个流行的 Flux 库——我写它的时候我正在做我的ReactEurope 演讲,关于带时间旅行的热重载我有一个主要目标:通过删除动作来动态更改减速器代码甚至“改变过去”,并查看重新计算的状态成为可能。

我还没有看到一个能够做到这一点的 Flux 库。React Hot Loader 也不允许你这样做——事实上,如果你编辑 Flux 存储,它会中断,因为它不知道如何处理它们。

当 Redux 需要重新加载 reducer 代码时,它会调用replaceReducer(),然后应用程序使用新代码运行。在 Flux 中,数据和函数纠缠在 Flux 存储中,所以你不能“只是替换函数”。此外,您必须以某种方式向 Dispatcher 重新注册新版本——Redux 甚至没有。

生态系统

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

简单

Redux 保留了 Flux 的所有优点(记录和重放动作、单向数据流、依赖突变)并增加了新的优点(容易撤销重做、热重载),而没有引入 Dispatcher 和存储注册。

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

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

您实际上可以阅读它并了解所有 Redux。


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

我基于 Fluxxor(基本上是纯通量)开始了我的 Android/Java 实现(Fluxxan)。一旦我看到 redux,我就被卖了。有些部分我完全保持了流动性,但是伙计,你的库很棒!
2021-03-18 10:34:54
这行github.com/reactjs/redux/blob/...是我搜索了一周的问题的答案:如何构建 store 和 reducer,以便可以在不重复的情况下处理在不同上下文中使用的多个可重用组件实例逻辑。答案似乎是:使用三层深度存储:第一层:组件名称(“分页”),第二层:容器名称(“stargazersByRepo”),第三层:容器的唯一键/ID(${login}/${name})。非常感谢你!
2021-03-18 10:34:54
感谢您的回答...我是 js 的新手...在您的回答中,您说过通量正在使用单例设计模式...你能在 redux 中告诉我他们正在使用什么样的设计模式...并且在通量中可以你告诉我他们在哪里使用单例模式......你能举一个例子......我从这里理解设计模式是什么单例模式
2021-04-04 10:34:54
你想学习 Redux 吗?只需观看此视频:youtube.com/watch? v= ucd5x3Ka3gw
2021-04-08 10:34:54
我们选择了 redux,因为它比flux 更自以为是。我们一直在争论某些代码应该如何/去哪里,等等。Redux 为我们消除了所有这些困惑。我们已经使用 redux 为 web 和 react-native 构建了应用程序,这太棒了!!
2021-04-11 10:34:54

在 Quora,有人说

首先,完全可以在没有 Flux 的情况下使用 React 编写应用程序。

还有这个我创建的可视化图表来显示两者的快速视图,对于不想阅读整个解释的人来说可能是一个快速的答案: Flux 与 Redux

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

我相信你应该从纯 React 开始,然后学习 Redux 和 Flux。在你对 React 有了一些真正的体验后,你会看到 Redux 是否对你有帮助。

也许你会觉得 Redux 正是你的应用程序,也许你会发现,Redux 正试图解决一个你并没有真正遇到的问题。

如果您直接从 Redux 开始,与没有 Redux 相比,您最终可能会得到过度设计的代码、更难维护的代码以及更多的错误。

来自Redux 文档

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

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

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

这种复杂性很难处理,因为我们混合了两个人类思维很难推理的概念:变异和异步。我称它们为曼妥思和可乐。两者分开时都很棒,但它们在一起会造成混乱。像 React 这样的库试图通过移除异步和直接 DOM 操作来解决视图层中的这个问题。但是,管理数据状态取决于您。这就是 Redux 的用武之地。

跟随 Flux、CQRS 和事件溯源的脚步,Redux 尝试通过对更新发生的方式和时间施加某些限制来使状态变化可预测。这些限制体现在 Redux 的三个原则中。

同样来自Redux 文档

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

想象一下,您的应用程序的状态被描述为一个普通对象。例如,todo 应用程序的状态可能如下所示:

{
  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 的这篇文章,他在其中讨论了 Flux 的各种实现及其在编写 redux 时的权衡: Flux 框架的演变

其次,您链接到的动机页面并没有真正讨论 Redux 的动机,而是讨论 Flux(和 React)背后的动机。三原则是更终极版特定但仍无法应付来自标准通量架构的实现差异。

基本上,Flux 有多个 store,它们计算状态变化以响应 UI/API 与组件的交互,并将这些变化作为组件可以订阅的事件进行广播。在 Redux 中,每个组件都只订阅一个 store。IMO 感觉至少 Redux 通过统一(或减少,如 Redux 所说)返回组件的数据流来进一步简化和统一数据流 - 而 Flux 专注于统一数据流的另一端 - 查看模型。

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

由于我的谈话有点晚了,我只想指出,尽管我抱有最大的希望,但 Facebook 似乎认为他们的 Flux 实现是概念证明,但它从未得到应有的关注。

我鼓励你使用它,因为它揭示了 Flux 架构的更多内部工作,这很有教育意义,但同时它没有提供像 Redux 这样的库提供的许​​多好处(这些不是这对小项目很重要,但对大项目非常有value)。

我们决定继续前进,我们将转向 Redux,我建议您也这样做;)

我写了一些简单的帮助程序来处理 FBFlux,它实际上似乎比我找到的所有示例 Redux 应用程序更少样板和应用程序设置。两个开发人员在一个应用程序上工作了 9 个月以上,并且从未遇到任何架构问题。
2021-03-23 10:34:54
@VolodymyrBakhmatiuk 对我们来说主要是减少我们必须编写的样板数量+更好的错误处理(例如,如果您触发了一个未在常量列表中定义的操作,redux 会大喊大叫 - FB 流量不会,它可能导致所有各种各样的问题)在flux中有一些更高级的功能,但我还没有使用它们
2021-03-31 10:34:54
我已经开发 Facebook Flux 应用程序六个月了。而且我仍然不确定迁移时间是否值得 Redux 提供的好处。我将非常感谢您对 Redux 相对于 FB 流量的优缺点的所有评论!
2021-04-02 10:34:54
@DominiquePERETTI - 是的(也可以使用 linting),但它并没有改变在运行时没有捕捉到错误是有点可悲的事实
2021-04-07 10:34:54
@GuyNesher 应在编译时而不是在运行时检测到未定义的操作。Flow(另一个 Facebook 贡献)允许你这样做。
2021-04-10 10:34:54

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

有关Flux 与 Redux 的更多信息

关于多个商店,现在在 Redux 中是可行的,在 react-redux 中,您可以添加一个密钥来隔离商店:redux.js.org/faq/storesetup工作示例:github.com/Lemoncode/redux-multiple-stores
2021-03-31 10:34:54