【问题标题】:Redux - multiple stores, why not?Redux - 多个商店,为什么不呢?
【发布时间】:2016-02-10 17:27:40
【问题描述】:

作为说明:我已经阅读了 Redux 的文档(也是猴面包树),并且我已经完成了相当多的谷歌搜索和测试。

为什么强烈建议 Redux 应用只有一个商店?

我了解单店设置与多店设置的优缺点(关于这个主题有很多关于 SO 的问答)。

IMO,此架构决策属于应用开发人员根据其项目的需求。那么为什么强烈建议 Redux 使用它,几乎到了听起来是强制性的程度(尽管没有什么能阻止我们创建多个商店)?

编辑:转换为单店后的反馈

在与 redux 合作几个月后,在许多人认为复杂的 SPA 上,我可以说单一商店结构是一种纯粹的工作乐趣。

以下几点可能有助于其他人理解为什么在很多很多用例中单店与多店是一个没有实际意义的问题:

  • 可靠:我们使用选择器来挖掘应用程序状态并获取与上下文相关的信息。我们知道所有需要的数据 在一个商店里。它避免了所有关于状态在哪里的问题 问题可能是。
  • 很快:我们的商店目前有将近 100 个减速器,如果不是更多的话。即使这样算,也只有少数 reducer 处理数据 任何给定的调度,其他的只是返回之前的状态。这 一个巨大/复杂的存储(nbr of reducers)很慢的论点是 几乎没有实际意义。至少我们没有看到任何性能问题 从那里来。
  • 调试友好:虽然这是将 redux 作为一个整体使用的最有说服力的论点,但它也适用于单存储与多存储 店铺。在构建应用程序时,您肯定会在 进程(程序员错误),这是正常的。 PITA 是当那些 错误需要数小时才能调试。感谢单一商店(和 redux-logger)我们从来没有在任何给定的 状态问题。

几点建议

构建 redux 商店的真正挑战在于决定如何构建它。首先,因为在路上改变结构只是一个主要的痛苦。其次,因为它在很大程度上决定了您将如何使用以及查询您的应用程序数据以获取任何进程。关于如何构建商店有很多建议。在我们的案例中,我们发现以下是理想的:

{
  apis: {     // data from various services
    api1: {},
    api2: {},
    ...
  }, 
  components: {} // UI state data for each widget, component, you name it 
  session: {} // session-specific information
}

希望此反馈对其他人有所帮助。

编辑 2 - 有用的商店工具

对于那些一直想知道如何“轻松”管理单一商店的人来说,这很快就会变得复杂。有一种工具可以帮助隔离商店的结构依赖关系/逻辑。

Normalizr 可以根据架构规范化您的数据。然后它提供一个接口来处理您的数据并通过id 获取数据的其他部分,就像字典一样。

当时我不知道 Normalizr,但我按照相同的思路构建了一些东西。 relational-json 接受一个模式,并返回一个基于表的接口(有点像数据库)。关系 json 的优点是您的数据结构可以动态引用数据的其他部分(本质上,您可以在任何方向上遍历数据,就像普通的 JS 对象)。它不如 Normalizr 成熟,但我已经在生产环境中成功使用了几个月。

【问题讨论】:

  • 我喜欢您使用的商店结构的方法;但是,您如何处理映射到组件状态更改的 api 状态更改?假设我从我的 API 接收特定领域的数据,这如何转化为可以在我的组件中找到的通用数据结构?
  • 您的组件如何映射/使用存储数据完全取决于您。虽然我认为我不完全理解您的问题,但您能否详细说明或开始聊天会话?
  • 我想问题是:您的组件是从 apis 状态渲染任何内容,还是只渲染放入组件状态的任何内容。我怀疑如果您设法仅从组件的状态进行渲染,那么您已经找到了一种极好的方法来使您的组件和容器即使在存在域特定数据的情况下也具有高度可重用性。如果您的组件部分从 API 状态和组件状态呈现,那么我猜您正在利用特定于域的容器将 apis 中的数据映射到您的组件理解的通用列表和原语。
  • 我将 Redux 与选择器结合使用,选择器基本上是记忆化的功能纯数据映射器。每个组件“做出反应”以存储更新,如果更改与它相关,它会“选择”数据并相应地呈现。所以是的,组件仅根据对他们而言重要的内容进行渲染。但这不仅仅是因为 Redux 或存储结构。这是由于结合了不可变数据存储、数据更改的引用比较测试以及以组件需要的格式获取组件所需数据的纯选择器。
  • 嘿@SebastienDaniel,您能否举例说明您如何实施检查每个组件以了解商店更新中的更改是否与其相关?我的意思是,如果您使用的是某种通用模式……或者在每个特定情况下,您都在检查特定组件相关数据是否已更改。

标签: javascript redux


【解决方案1】:

为什么我们不能使用 redux 使用多个商店????

这在 Redux 中不是必需的,因为数据域之间的分离已经通过将单个 reducer 拆分为更小的来实现 减速器。


我可以或应该创建多个商店吗?我可以直接导入我的商店,然后自己在组件中使用它吗?

最初的 Flux 模式描述了在一个应用程序中有多个“商店”,每个商店都拥有不同区域的域数据。这可能会引入一些问题,例如需要让一个商店“等待”另一个商店进行更新。

这在 Redux 中不是必需的,因为数据域之间的分离已经通过将单个 reducer 拆分为更小的来实现 减速器。

与其他几个问题一样,可以在一个页面中创建多个不同的 Redux 存储,但预期的模式是只有一个存储。拥有一个单独的 store 可以使用 Redux DevTools,使数据的持久化和再水化更简单,并简化订阅逻辑。

在 Redux 中使用多个商店的一些正当理由可能包括:

通过分析应用程序确认时,解决了由于状态的某些部分更新过于频繁而导致的性能问题。 将 Redux 应用程序隔离为更大应用程序中的组件,在这种情况下,您可能希望为每个根组件实例创建一个存储。 但是,创建新商店不应该是您的第一直觉,尤其是如果您来自 Flux 背景。先尝试reducer组合,如果不能解决你的问题,只使用多个store。

同样,虽然您可以通过直接导入来引用您的商店实例,但这不是 Redux 中推荐的模式。如果您创建一个商店实例并从一个模块中导出它,它将成为一个单例。这意味着将 Redux 应用程序隔离为更大应用程序的组件(如果有必要)或启用服务器渲染将更加困难,因为您希望在服务器上为每个请求创建单独的存储实例。

official doc by redux

【讨论】:

    【解决方案2】:

    此架构决策属于应用开发者,基于 他们项目的需求

    你生活在你自己的世界里。我正在与使用 redux 的人会面,因为它很流行,每天。你甚至无法想象有多少项目是在没有任何决定的情况下开始减少的。我讨厌 redux 方法,但不得不使用它,因为其他开发人员一无所知。这只是一个被 facebook 夸大的史诗泡沫。

    • 不可靠,因为商店的某些部分不是孤立的。
    • 效率低下,因为您正在克隆和遍历哈希树。当突变以算术方式增长时 - 复杂性以几何方式增长。你无法通过重构任何 reducer、选择器等来解决它。你必须拆分你的 trie。
    • 当它变慢时没有人愿意将其拆分为具有单独存储的单独应用程序。没有人愿意在重构上花钱。人们通常将一些智能组件转换为转储,仅此而已。你知道等待 redux 开发者的未来是什么吗?他们将维持这些地狱。
    • 调试不友好。很难调试商店虚拟隔离部分之间的连接。甚至很难分析这些连接的数量。

    假设您有几个 redux 商店。您将中断单向数据流。您将立即意识到您在商店之间拥有多少联系。您可能会受到这些联系、与循环部门的斗争等的影响。

    具有单向流动的单一不可变存储并不是所有疾病的灵丹妙药。如果您不想维护项目架构,那么您无论如何都会受苦。

    【讨论】:

    • 我喜欢你说的,here我问过相关问题。你介意有时间看看这个并分享你的意见吗?我在 Reddit 上提出的问题,因为这里不鼓励这样的问题。
    【解决方案3】:

    在某些极端情况下,您可能会使用多个商店(例如,如果您在每秒多次同时更新屏幕上的数千个项目的列表时遇到性能问题)。也就是说,这是一个例外,在大多数应用中,您永远只需要一个商店。

    为什么我们在文档中强调这一点?因为大多数来自 Flux 背景的人会假设多个商店是使更新代码模块化的解决方案。但是 Redux 对此有不同的解决方案:reducer 组合。

    将多个 reducer 进一步拆分为一个 reducer 树是您在 Redux 中保持更新模块化的方式。如果你没有意识到这一点,在没有完全了解 reducer 组成的情况下就去多个 store,你会错过 Redux 单 store 架构的许多好处:

    • 使用 reducer 组合可以很容易地在 Flux 中实现“依赖更新”,例如 waitFor,方法是编写一个 reducer,手动调用其他 reducer,并以特定的顺序提供附加信息。

    • 使用单个存储,很容易持久化、补充和读取状态。服务器渲染和数据预取是微不足道的,因为只有一个数据存储需要在客户端进行填充和再水化,而 JSON 可以描述其内容而无需担心存储的 ID 或名称。

    • 单个商店使 Redux DevTools 时间旅行功能成为可能。它还使 redux-undo 或 redux-optimist 等社区扩展变得容易,因为它们在 reducer 级别上运行。这样的“reducer enhancers”不能写给stores。

    • 单一存储保证仅在处理了调度后才调用订阅。也就是说,在通知侦听器时,状态已完全更新。对于许多商店,没有这样的保证。这是 Flux 需要waitFor 拐杖的原因之一。对于单一商店,这不是您首先看到的问题。

    • 最重要的是,在 Redux 中不需要多个存储(除非您应该首先分析性能边缘情况)。我们在文档中强调了这一点,因此鼓励您学习 reducer 组合和其他 Redux 模式,而不是像使用 Flux 一样使用 Redux,从而失去其优势。

    【讨论】:

    • 我承认我不了解减速器组合的全部优势/必要性。感谢您的回答,我做了更多阅读和一个示例(TodoMVC,再次)。对于这样一个小例子,很难理解减速器组合所提供的实际改进。然而,稍加思考,在大范围内,收益(现在)是显而易见的。再次感谢,很好的答案!
    • @Sebastien 我认为“购物车”的例子更好。
    • 我正在慢慢地将 redux 实施到传统(非 SPA)应用程序中。我为每个转换为 react/redux 实现的“整体”使用 multilpe 商店,直到可以修改整个应用程序以使用同一个商店。
    • @DanAbramov 很好奇如果您的主“应用程序”运行自己的 Redux 商店并通过 npm 导入运行自己的独立 Redux 商店的独立“应用程序”,您会怎么做。例如,如果您公司中的其他团队有某种消息传递服务,并且您希望在不使用该数据污染您的商店的情况下拉入 UI。
    • @natlee75 "在 Redux 中使用多个商店的一些正当理由可能包括:[...] 将 Redux 应用程序隔离为更大应用程序中的组件,在这种情况下,您可能想要创建一个商店每个根组件实例。”来自redux.js.org/docs/FAQ.html#store-setup-multiple-stores
    【解决方案4】:

    在很多情况下,在 Redux 中拥有一个商店确实是我们所需要的,我同时使用了 Redux 和 Flux,并且相信 Redux 做得更好!

    不要忘记 store 是在一个 JavaScript 对象中,所以当你只有一个 store 时,它​​可以很容易地扩展和重用,对我来说,拥有一个 store 可以更容易地使用 Redux 开发工具遍历而不是混在大型应用程序中...

    此外,一个商店的概念是为我们模仿数据库,这是一个事实来源,您可以更改它并且可以在浏览器内存中访问它...

    如果整个应用管理好,一个商店就足以管理整个应用状态...

    【讨论】:

    • 所以每个人都应该相信单店会做得更好,没有人能解释为什么。它让我想起了一些事情......
    【解决方案5】:

    在一些具有成百上千个 reducer 的大型企业应用程序中,将应用程序的不同区域视为完全独立的应用程序通常很有用。在那些情况下(实际上是多个应用程序共享一个域名),我会使用多个商店。

    例如,我倾向于将以下常见功能区域视为单独的应用程序:

    • 管理员
    • 分析/数据与仪表板
    • 计费管理和采购流程
    • 企业客户团队/权限管理

    如果这些东西很小,只需将它们作为主应用程序的一部分。如果它们变得非常大(就像某些企业帐户管理和分析工具所做的那样),请将它们分开。

    管理超大型应用的最佳方式是将它们视为许多较小应用的组合。

    如果您的应用小于约 50k LOC,您可能应该忽略此建议并遵循 Dan 的建议。

    如果您的应用程序的 LOC 超过 100 万,您可能应该拆分迷你应用程序,即使您在单一存储库中维护它们。

    【讨论】:

      【解决方案6】:

      多个商店在以下用例中可能会有所帮助 1. 如果你有大型组件,它们在数据结构、行为、应用程序上下文方面相互独立。隔离这些组件可以更轻松地管理您的数据和应用程序流。它还有助于独立开发和维护您的组件。 2. 性能问题:不是典型的用例,但是如果你的某些组件更新非常频繁,对其他组件没有任何影响,可能你可以去不同的商店。

      对于所有其他情况,您可能不需要拥有多个商店。正如 Dan 所说,创建周到的 reducer 组合可以证明是更好的解决方案。

      【讨论】:

      • 您的消息看起来像“除少数情况外始终使用 redux”==“除少数情况外,您不需要项目架构”。非常接近现实,我很高兴。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-05-07
      • 1970-01-01
      • 1970-01-01
      • 2017-06-23
      • 2016-09-30
      • 2020-02-06
      相关资源
      最近更新 更多