Redux - 多个商店,为什么不呢?
Redux - multiple stores, why not?
注意:我已经阅读了 Redux 的文档(还有 Baobab),并且我已经完成了大量的谷歌搜索和测试。
为什么强烈建议 Redux 应用只有一个商店?
我了解 pros/cons 单店设置与多店设置的区别(关于此主题的 SO 有很多问答)。
IMO,此架构决定属于应用程序开发人员根据其项目需求做出的决定。那么为什么对 Redux 如此强烈建议,几乎到了强制性的地步(虽然没有什么能阻止我们建立多个商店)?
编辑:转换为单店后的反馈
在许多人认为是复杂 SPA 的 redux 上工作了几个月之后,我可以说单一商店结构是一种纯粹的工作乐趣。
有几点可以帮助其他人理解为什么单店与多店在许多用例中是一个没有实际意义的问题:
- 可靠:我们使用选择器来挖掘应用程序状态并获取上下文相关信息。我们知道所有需要的数据
是在一个单一的商店。它避免了关于状态在哪里的所有质疑
问题可能是。
- 它很快:我们的商店目前有近 100 个减速器,如果不是更多的话。即使如此,也只有少数减速器处理数据
任何给定的调度,其他只是 return 以前的状态。这
huge/complex 存储(nbr of reducers)速度慢的论点是
几乎没有实际意义。至少我们没有看到任何性能问题
来自那里。
- 调试友好:虽然这是将 redux 作为一个整体使用的最有说服力的论据,但它也适用于单店与多店
店铺。在构建应用程序时,您一定会遇到状态错误
过程(程序员错误),这是正常的。 PITA 是当那些
错误需要数小时才能调试。感谢单店(和
redux-logger) 我们从来没有花超过几分钟的时间在任何给定的
状态问题。
几点指点
构建 redux store 的真正挑战在于决定如何构建它。首先,因为在未来改变结构只是一个主要的痛苦。其次,因为它在很大程度上决定了您将如何使用和查询任何进程的应用程序数据。关于如何构建商店有很多建议。在我们的例子中,我们发现以下是理想的:
{
apis: { // data from various services
api1: {},
api2: {},
...
},
components: {} // UI state data for each widget, component, you name it
session: {} // session-specific information
}
希望此反馈对其他人有所帮助。
编辑 2 - 有用的商店工具
对于那些一直想知道如何 "easily" 管理 单一商店 的人,这很快就会变得复杂。有一种工具可以帮助隔离商店的结构 dependencies/logic。
Normalizr 可以根据模式规范化您的数据。然后它提供了一个接口来处理您的数据并通过 id
获取数据的其他部分,就像字典一样。
当时还不知道 Normalizr,我按照同样的思路构建了一些东西。 relational-json 采用模式,return 是基于 Table 的接口(有点像数据库)。 relational-json的优点是你的数据结构动态引用你数据的其他部分(本质上,你可以在任何方向遍历你的数据,就像普通的JS对象) .它不如 Normalizr 成熟,但我已经在生产中成功使用它几个月了。
在某些情况下,您可能会使用多个商店(例如,如果您在每秒多次更新屏幕上同时显示的数千个项目的列表时遇到性能问题)。也就是说,这是一个例外,在大多数应用程序中,您只需要一个商店。
为什么我们在文档中强调这一点?因为大多数来自 Flux 背景的人会 假设多个存储是使更新代码模块化的解决方案。然而 Redux 对此有不同的解决方案:reducer composition.
将多个 reducer 进一步拆分成一个 reducer 树是您在 Redux 中保持更新模块化的方式。如果你不认识这一点,在没有完全理解 reducer 组成的情况下就去使用多个 store,你将错过 Redux 单 store 架构的许多好处:
使用 reducer 组合可以很容易地在 Flux 中实现 "dependent updates" a la waitFor
=12=]
单店,很容易坚持,补水,看状态。服务器渲染和数据预取是微不足道的,因为只有一个数据存储需要在客户端进行填充和再水化,JSON 可以描述其内容而无需担心存储的 ID 或名称。
单个商店使 Redux DevTools 时间旅行功能成为可能。它还使 redux-undo 或 redux-optimist 等社区扩展变得容易,因为它们在 reducer 级别上运行。这样的"reducer enhancers"不能写店家
单个存储保证只有在处理完调度后才会调用订阅。也就是说,在通知侦听器时,状态已经完全更新。许多商店没有这样的保证。这是 Flux 需要 waitFor
拐杖的原因之一。对于单个商店,这不是您首先看到的问题。
最重要的是,在 Redux 中不需要多个存储(除了你应该首先分析的性能边缘情况)。我们在文档中将其作为重点,因此鼓励您学习 reducer 组合和其他 Redux 模式,而不是像使用 Flux 一样使用 Redux,并失去它的好处。
多个存储在以下用例中会有所帮助
1. 如果你有大型组件,它们在数据结构、行为、应用程序上下文方面相互独立。隔离这些组件可以更轻松地管理您的数据和应用程序流。它还有助于独立开发和维护您的组件。
2. 性能问题:不是典型的用例,但是如果你的一些组件更新非常频繁并且对其他组件没有任何影响,可能你可以去不同的商店。
对于所有其他情况,您可能不需要拥有多个商店。正如 Dan 所说,创建深思熟虑的减速器组合可以证明是更好的解决方案。
在一些拥有成百上千个 reducer 的超大型企业应用程序中,将应用程序的不同区域视为完全独立的应用程序通常很有用。在那些情况下(实际上是多个应用程序共享一个域名),我使用多个商店。
例如,我倾向于将以下常用功能区域视为单独的应用程序:
- 管理员
- 分析/数据可视化仪表板
- 计费管理和购买流程
- 企业帐号team/permission管理
如果这些东西中的任何一个很小,只需将它们作为主应用程序的一部分。如果它们变得非常大(如某些企业帐户管理和分析工具所做的那样),请将它们分开。
管理超大型应用程序的最佳方式是将它们视为许多小型应用程序的组合。
如果您的应用少于 ~50k LOC,您可能应该忽略此建议并遵循 Dan 的建议。
如果您的应用程序超过 100 万行代码,您可能应该拆分出迷你应用程序,即使您将它们维护在单一存储库中。
在很多情况下,在 Redux 中拥有一个商店确实是我们所需要的,我都使用过 Redux 和 Flux,并且相信 Redux 做得更好!
不要忘记商店在一个 JavaScript 对象中,所以当你只有一个商店时,它可以很容易地扩展和重用,对我来说,拥有一个商店可以更容易地遍历使用Redux 开发工具,不要混在大型应用程序中...
此外,一个商店的概念是为我们模仿数据库,这是一个您可以更改它并且可以在浏览器内存中访问它的真实来源...
如果整个应用管理得很好,一个商店就可以管理整个应用状态...
This architectural decision belongs to the app developers based on
their projects' needs
你活在自己的世界里。我正在与使用 redux 的人会面,因为它每天都很流行。你甚至无法想象有多少项目在没有任何决定的情况下就开始了 redux。我讨厌 redux 方法但不得不使用它,因为其他开发人员一无所知。这只是一个被 facebook 吹大的史诗般的泡沫。
- 不靠谱因为部分商店没有隔离。
- 效率很低 因为你在克隆和遍历哈希树。当突变以算术方式增长时 - 复杂性以几何方式增长。您无法通过重构任何缩减器、选择器等来修复它。您必须拆分您的 trie。
- 当它变慢时 没有人愿意将它拆分成具有单独存储的单独应用程序。没有人愿意花钱进行重构。人们通常 将一些智能组件转换为转储 仅此而已。你知道 redux 开发者的未来是什么吗?他们会维护这些地狱。
- 调试不友好。很难调试商店中几乎孤立的部分之间的连接。甚至很难分析这些连接的数量。
假设您有多个 redux 商店。您将打破单向数据流。您会立即意识到您的商店之间有多少联系。你可能会因为这些联系而受苦,与循环部门斗争等等
具有单向流的单一不可变存储并不是包治百病的灵丹妙药。如果你不想维护项目架构,你无论如何都会受苦。
为什么我们不能使用 redux 使用多个存储????
This is not necessary in Redux because the separation between data domains is already achieved by splitting a single reducer into smaller
reducers.
我可以或应该创建多个商店吗?我可以直接导入我的商店,然后自己在组件中使用它吗?
最初的 Flux 模式描述了在一个应用程序中有多个“商店”,每个商店拥有不同区域的域数据。这可能会引入一些问题,例如需要让一家商店“等待”另一家商店更新。
This is not necessary in Redux because the separation between data domains is already achieved by splitting a single reducer into smaller
reducers.
与其他几个问题一样,可以在一个页面中创建多个不同的 Redux 商店,但预期的模式是只有一个商店。拥有单个商店可以使用 Redux DevTools,使数据的持久化和再水化更简单,并简化订阅逻辑。
在 Redux 中使用多个存储的一些正当理由可能包括:
解决了由于状态的某些部分更新过于频繁而导致的性能问题,当通过分析应用程序确认时。
将 Redux 应用程序隔离为更大应用程序中的组件,在这种情况下,您可能希望为每个根组件实例创建一个商店。
但是,创建新商店不应该是您的第一直觉,尤其是如果您有 Flux 背景。先尝试 reducer composition,如果不能解决你的问题,才使用 multiple store。
同样,虽然您可以通过直接导入来引用您的商店实例,但这不是 Redux 中推荐的模式。如果您创建一个商店实例并将其从模块中导出,它将成为一个单例。这意味着将更难将 Redux 应用程序隔离为更大应用程序的组件(如果有必要)或启用服务器呈现,因为在服务器上您希望为每个请求创建单独的商店实例。
注意:我已经阅读了 Redux 的文档(还有 Baobab),并且我已经完成了大量的谷歌搜索和测试。
为什么强烈建议 Redux 应用只有一个商店?
我了解 pros/cons 单店设置与多店设置的区别(关于此主题的 SO 有很多问答)。
IMO,此架构决定属于应用程序开发人员根据其项目需求做出的决定。那么为什么对 Redux 如此强烈建议,几乎到了强制性的地步(虽然没有什么能阻止我们建立多个商店)?
编辑:转换为单店后的反馈
在许多人认为是复杂 SPA 的 redux 上工作了几个月之后,我可以说单一商店结构是一种纯粹的工作乐趣。
有几点可以帮助其他人理解为什么单店与多店在许多用例中是一个没有实际意义的问题:
- 可靠:我们使用选择器来挖掘应用程序状态并获取上下文相关信息。我们知道所有需要的数据 是在一个单一的商店。它避免了关于状态在哪里的所有质疑 问题可能是。
- 它很快:我们的商店目前有近 100 个减速器,如果不是更多的话。即使如此,也只有少数减速器处理数据 任何给定的调度,其他只是 return 以前的状态。这 huge/complex 存储(nbr of reducers)速度慢的论点是 几乎没有实际意义。至少我们没有看到任何性能问题 来自那里。
- 调试友好:虽然这是将 redux 作为一个整体使用的最有说服力的论据,但它也适用于单店与多店 店铺。在构建应用程序时,您一定会遇到状态错误 过程(程序员错误),这是正常的。 PITA 是当那些 错误需要数小时才能调试。感谢单店(和 redux-logger) 我们从来没有花超过几分钟的时间在任何给定的 状态问题。
几点指点
构建 redux store 的真正挑战在于决定如何构建它。首先,因为在未来改变结构只是一个主要的痛苦。其次,因为它在很大程度上决定了您将如何使用和查询任何进程的应用程序数据。关于如何构建商店有很多建议。在我们的例子中,我们发现以下是理想的:
{
apis: { // data from various services
api1: {},
api2: {},
...
},
components: {} // UI state data for each widget, component, you name it
session: {} // session-specific information
}
希望此反馈对其他人有所帮助。
编辑 2 - 有用的商店工具
对于那些一直想知道如何 "easily" 管理 单一商店 的人,这很快就会变得复杂。有一种工具可以帮助隔离商店的结构 dependencies/logic。
Normalizr 可以根据模式规范化您的数据。然后它提供了一个接口来处理您的数据并通过 id
获取数据的其他部分,就像字典一样。
当时还不知道 Normalizr,我按照同样的思路构建了一些东西。 relational-json 采用模式,return 是基于 Table 的接口(有点像数据库)。 relational-json的优点是你的数据结构动态引用你数据的其他部分(本质上,你可以在任何方向遍历你的数据,就像普通的JS对象) .它不如 Normalizr 成熟,但我已经在生产中成功使用它几个月了。
在某些情况下,您可能会使用多个商店(例如,如果您在每秒多次更新屏幕上同时显示的数千个项目的列表时遇到性能问题)。也就是说,这是一个例外,在大多数应用程序中,您只需要一个商店。
为什么我们在文档中强调这一点?因为大多数来自 Flux 背景的人会 假设多个存储是使更新代码模块化的解决方案。然而 Redux 对此有不同的解决方案:reducer composition.
将多个 reducer 进一步拆分成一个 reducer 树是您在 Redux 中保持更新模块化的方式。如果你不认识这一点,在没有完全理解 reducer 组成的情况下就去使用多个 store,你将错过 Redux 单 store 架构的许多好处:
使用 reducer 组合可以很容易地在 Flux 中实现 "dependent updates" a la
waitFor
=12=]单店,很容易坚持,补水,看状态。服务器渲染和数据预取是微不足道的,因为只有一个数据存储需要在客户端进行填充和再水化,JSON 可以描述其内容而无需担心存储的 ID 或名称。
单个商店使 Redux DevTools 时间旅行功能成为可能。它还使 redux-undo 或 redux-optimist 等社区扩展变得容易,因为它们在 reducer 级别上运行。这样的"reducer enhancers"不能写店家
单个存储保证只有在处理完调度后才会调用订阅。也就是说,在通知侦听器时,状态已经完全更新。许多商店没有这样的保证。这是 Flux 需要
waitFor
拐杖的原因之一。对于单个商店,这不是您首先看到的问题。最重要的是,在 Redux 中不需要多个存储(除了你应该首先分析的性能边缘情况)。我们在文档中将其作为重点,因此鼓励您学习 reducer 组合和其他 Redux 模式,而不是像使用 Flux 一样使用 Redux,并失去它的好处。
多个存储在以下用例中会有所帮助 1. 如果你有大型组件,它们在数据结构、行为、应用程序上下文方面相互独立。隔离这些组件可以更轻松地管理您的数据和应用程序流。它还有助于独立开发和维护您的组件。 2. 性能问题:不是典型的用例,但是如果你的一些组件更新非常频繁并且对其他组件没有任何影响,可能你可以去不同的商店。
对于所有其他情况,您可能不需要拥有多个商店。正如 Dan 所说,创建深思熟虑的减速器组合可以证明是更好的解决方案。
在一些拥有成百上千个 reducer 的超大型企业应用程序中,将应用程序的不同区域视为完全独立的应用程序通常很有用。在那些情况下(实际上是多个应用程序共享一个域名),我使用多个商店。
例如,我倾向于将以下常用功能区域视为单独的应用程序:
- 管理员
- 分析/数据可视化仪表板
- 计费管理和购买流程
- 企业帐号team/permission管理
如果这些东西中的任何一个很小,只需将它们作为主应用程序的一部分。如果它们变得非常大(如某些企业帐户管理和分析工具所做的那样),请将它们分开。
管理超大型应用程序的最佳方式是将它们视为许多小型应用程序的组合。
如果您的应用少于 ~50k LOC,您可能应该忽略此建议并遵循 Dan 的建议。
如果您的应用程序超过 100 万行代码,您可能应该拆分出迷你应用程序,即使您将它们维护在单一存储库中。
在很多情况下,在 Redux 中拥有一个商店确实是我们所需要的,我都使用过 Redux 和 Flux,并且相信 Redux 做得更好!
不要忘记商店在一个 JavaScript 对象中,所以当你只有一个商店时,它可以很容易地扩展和重用,对我来说,拥有一个商店可以更容易地遍历使用Redux 开发工具,不要混在大型应用程序中...
此外,一个商店的概念是为我们模仿数据库,这是一个您可以更改它并且可以在浏览器内存中访问它的真实来源...
如果整个应用管理得很好,一个商店就可以管理整个应用状态...
This architectural decision belongs to the app developers based on their projects' needs
你活在自己的世界里。我正在与使用 redux 的人会面,因为它每天都很流行。你甚至无法想象有多少项目在没有任何决定的情况下就开始了 redux。我讨厌 redux 方法但不得不使用它,因为其他开发人员一无所知。这只是一个被 facebook 吹大的史诗般的泡沫。
- 不靠谱因为部分商店没有隔离。
- 效率很低 因为你在克隆和遍历哈希树。当突变以算术方式增长时 - 复杂性以几何方式增长。您无法通过重构任何缩减器、选择器等来修复它。您必须拆分您的 trie。
- 当它变慢时 没有人愿意将它拆分成具有单独存储的单独应用程序。没有人愿意花钱进行重构。人们通常 将一些智能组件转换为转储 仅此而已。你知道 redux 开发者的未来是什么吗?他们会维护这些地狱。
- 调试不友好。很难调试商店中几乎孤立的部分之间的连接。甚至很难分析这些连接的数量。
假设您有多个 redux 商店。您将打破单向数据流。您会立即意识到您的商店之间有多少联系。你可能会因为这些联系而受苦,与循环部门斗争等等
具有单向流的单一不可变存储并不是包治百病的灵丹妙药。如果你不想维护项目架构,你无论如何都会受苦。
为什么我们不能使用 redux 使用多个存储????
This is not necessary in Redux because the separation between data domains is already achieved by splitting a single reducer into smaller reducers.
我可以或应该创建多个商店吗?我可以直接导入我的商店,然后自己在组件中使用它吗?
最初的 Flux 模式描述了在一个应用程序中有多个“商店”,每个商店拥有不同区域的域数据。这可能会引入一些问题,例如需要让一家商店“等待”另一家商店更新。
This is not necessary in Redux because the separation between data domains is already achieved by splitting a single reducer into smaller reducers.
与其他几个问题一样,可以在一个页面中创建多个不同的 Redux 商店,但预期的模式是只有一个商店。拥有单个商店可以使用 Redux DevTools,使数据的持久化和再水化更简单,并简化订阅逻辑。
在 Redux 中使用多个存储的一些正当理由可能包括:
解决了由于状态的某些部分更新过于频繁而导致的性能问题,当通过分析应用程序确认时。 将 Redux 应用程序隔离为更大应用程序中的组件,在这种情况下,您可能希望为每个根组件实例创建一个商店。 但是,创建新商店不应该是您的第一直觉,尤其是如果您有 Flux 背景。先尝试 reducer composition,如果不能解决你的问题,才使用 multiple store。
同样,虽然您可以通过直接导入来引用您的商店实例,但这不是 Redux 中推荐的模式。如果您创建一个商店实例并将其从模块中导出,它将成为一个单例。这意味着将更难将 Redux 应用程序隔离为更大应用程序的组件(如果有必要)或启用服务器呈现,因为在服务器上您希望为每个请求创建单独的商店实例。