147

我正在研究一种中等复杂度的前端应用程序。目前它是用纯 javascript 编写的,它有很多不同的基于事件的消息连接这个应用程序的几个主要部分。

我们决定在进一步重构的范围内为此应用程序实现某种状态容器。以前我有一些使用 redux 和 ngrx store 的经验(实际上遵循相同的原则)。

Redux对我们来说是一种选择,但其中一位开发人员建议使用基于状态机的库,特别是xstate 库

我从未使用过 xstate,所以我发现它很有趣,并开始阅读文档并查看不同的示例。看起来很有前途和强大,但在某些时候我明白我没有看到它和 redux 之间有任何显着差异。

我花了几个小时试图找到答案,或者比较 xstate 和 redux 的任何其他信息。我没有找到任何明确的信息,除了一些文章,如“从 redux 到状态机” ,或者专注于同时使用 redux 和 xstate 的库的链接(很奇怪)。

如果有人可以描述差异或告诉我开发人员何时应该选择 xstate - 欢迎您。

4

3 回答 3

307

我创建了 XState,但我不会告诉你是否使用其中一个。这取决于你的团队。相反,我将尝试强调一些关键差异。

还原 状态
本质上是一个状态容器,其中事件(在 Redux 中称为操作)被发送到更新状态的 reducer 也是一个状态容器,但将有限状态(例如,,"loading""success"与“无限状态”或上下文(例如,items: [...])分开
不规定你如何定义你的减速器——它们是简单的函数,在给定当前状态和事件(动作)的情况下返回下一个状态 一个“有规则的减速器”——你定义了由于事件而导致的有限状态之间的合法转换,以及在转换中应该执行哪些动作(或在进入/退出状态时)
没有内置方法来处理副作用;有很多社区选项,比如 redux-thunk、redux-saga 等。 使动作(副作用)具有声明性和显式性 - 它们是State每次转换(当前状态 + 事件)返回的对象的一部分
目前无法可视化状态之间的转换,因为它无法区分有限状态和无限状态 有一个可视化工具:https ://statecharts.github.io/xstate-viz ,由于声明性,这是可行的
reducer 中表示的隐式逻辑/行为不能以声明方式序列化(例如,在 JSON 中) 机器定义,代表逻辑/行为,可以序列化为 JSON,并从 JSON 中读取;这使得行为非常可移植并且可以通过外部工具进行配置
不是严格意义上的状态机 严格遵守 W3C SCXML 规范:https ://www.w3.org/TR/scxml/
依靠开发人员手动防止不可能的状态 使用状态图自然定义处理事件的边界,防止不可能的状态,可以静态分析
鼓励使用单一的“全局”原子存储 鼓励使用类似 Actor 模型的方法,其中可以有许多相互通信的分层状态图/“服务”实例

本周我将在文档中添加更多关键差异。

于 2019-02-04T17:08:51.983 回答
29

状态机不会告诉(强制)您拥有单向数据流。它与数据流无关。它更多的是关于约束状态变化状态转换。因此,通常只有应用程序的某些部分会使用状态机设计,只有且仅当您需要约束/禁止某些状态更改并且您对转换感兴趣时。

请注意,对于状态机,如果由于某种原因(外部 API 依赖等...),应用程序可能会被锁定在由于限制而无法转换到另一个状态的状态,您必须解决它。

但是,如果您只对最后一个应用程序状态本身感兴趣,而不是状态转换,并且状态约束无关紧要,那么您最好不要使用状态机并直接更新状态本身(您仍然可以将状态包装在 Singleton 类中更新通过动作类)。


另一方面,Redux单向架构框架。单向架构强制您拥有单一方向的数据流。在 Redux 中,它以User->View->(Action)->Store->Reducer->(Middleware)->Store->(State)->View. 与状态机一样,您可以使用 Redux 中的中间件触发副作用。如果需要,您可以限制/禁止状态转换。与状态机不同,Redux 强制单向数据流,纯粹!reducer 函数、不可变状态对象、单个可观察的应用程序状态。

于 2019-06-26T07:45:35.430 回答
3

我的几点如下。

  • UI 状态和业务/后端状态在 redux 中耦合在一起。因此,对 ui 或业务状态的每次更新都会在 redux 存储中创建一个数据更新。
  • Xstate 将 UI 状态和后端状态解耦。
  • 在 redux 中,所有节点都存在于根节点内。Xstate 在独立的机器中分散和分发数据。
  • 应用程序只能在已经定义的状态之间转换。因此,任何错误或错误都可以在机器本身中修复。
  • 内部状态由机器本身在 Xstate 中管理。Redux 将新状态表示为标志。
  • Renderer agonistic - 保持尽可能多的状态提升到 Machines 中,如果需要,我们可以相对容易地切换渲染框架(例如从 react 到 vue)。
  • Contexts 提供了具体的类来向外界呈现一个单一的接口。
于 2021-06-14T15:34:28.693 回答