- 前端
- 后端
【免费下载链接】xstate
State machines, statecharts, and actors for complex logic
XState 是面向 JavaScript 和 TypeScript 应用的状态管理与编排解决方案,采用事件驱动编程、有限状态机(FSM)、Statechart(状态图)与 Actor 模型,以可预测、健壮且可视化的方式处理复杂逻辑。本文以本仓库根目录 README.md 为主线,结合 packages/core 的源码实现与 examples 中的真实示例,系统讲解如何选型、建模与运行状态机,帮助读者掌握从createMachine建模、createActor运行,到setup注入副作用、@xstate/store轻量状态管理的完整实战路径。
XState 是什么
XState 是一个面向 JS/TS 应用的状态管理和编排解决方案,核心包零依赖,既适用于前端也适用于后端应用逻辑(例如 examples/express-workflow 与 examples/mongodb-credit-check-api 展示了后端编排场景)。
它通过事件驱动编程、状态机、Statechart 与 Actor 模型,把应用逻辑建模为actors(参与者)和state machines(状态机),从而让复杂逻辑变得可预测、健壮且可视化。Statechart 是一种用于形式化建模有状态、响应式系统的体系,可以声明式地描述从单个组件到整体应用逻辑的行为。
从源码看,XState 的核心结构十分清晰(见 packages/core/src):
- createMachine.ts:根据配置创建“纯逻辑”的状态机(Statechart);
- createActor.ts:把逻辑实例化为可运行、可收发事件的 Actor;
- StateMachine.ts:状态机运行时,实现
ActorLogic接口; - setup.ts:集中声明 actors、actions、guards、delays 等实现,获得更强的类型推导;
- State.ts 与 stateUtils.ts:快照(Snapshot)与微观/宏观步进(microstep/macrostep)等转换机制。
XState 的设计受到 SCXML 规范 与之对应)。
我应该使用哪个包
根据需求选择,两者可以组合使用,但并非互相依赖:
| 包 | 适用场景 |
|---|---|
@xstate/store | 简单的事件驱动状态管理,体积 <1kb,TypeScript 类型推导优秀,思路接近 Redux/Zustand。如果你只需要一个 store,从这里开始。 |
xstate(核心包) | 状态机、Statechart、Actor、副作用与复杂应用逻辑编排 |
两者可以很好地配合使用,但不需要为了使用其中一个而安装另一个。当你的逻辑变复杂时,可以从@xstate/store平滑升级到完整的 XState 状态机。
超快速开始:第一个状态机
安装核心包:
npm install xstate下面是一个完整的计数器 Toggle 状态机(README 官方示例,仓库 examples/toggle/src/toggleMachine.ts 中也有无 context 的简化版本):
import { createMachine, createActor, assign } from 'xstate'; // 定义状态机(纯逻辑) const toggleMachine = createMachine({ id: 'toggle', initial: 'inactive', context: { count: 0 }, states: { inactive: { on: { TOGGLE: { target: 'active' } } }, active: { entry: assign({ count: ({ context }) => context.count + 1 }), on: { TOGGLE: { target: 'inactive' } } } } }); // 创建 Actor(状态机逻辑的实例,类似于 store) const toggleActor = createActor(toggleMachine); toggleActor.subscribe((state) => console.log(state.value, state.context)); toggleActor.start(); // => logs 'inactive', { count: 0 } toggleActor.send({ type: 'TOGGLE' }); // => logs 'active', { count: 1 } toggleActor.send({ type: 'TOGGLE' }); // => logs 'inactive', { count: 1 }从源码理解这套流程
createMachine只负责建模。源码注释明确指出:"The state machine represents the pure logic of a state machine actor"(状态机表示状态机 Actor 的纯逻辑),见 createMachine.ts。它接收MachineConfig,返回一个StateMachine实例。createActor负责运行。在 createActor.ts 中,createActor(logic, options)直接new Actor(logic, options)。Actor 是"一个正在运行的过程,可以接收事件、发送事件,并根据收到的事件改变自身行为,进而产生 Actor 外部的影响",见 createActor.ts。start()、send()、subscribe()构成运行闭环。start()会注册 actor、以初始化事件驱动首次快照更新并启动 mailbox(事件队列);send()通过this.system._relay把事件投入 mailbox,由_process调用logic.transition(snapshot, event, scope)计算下一个快照;subscribe()则把观察者登记到observers集合中,在update()里逐个通知,见 createActor.ts。assign是 context 更新机制。active状态的entry: assign(...)在进入该状态时执行,更新context.count。
核心 API 纵深:createMachine、createActor 与 setup
createMachine:声明式配置
createMachine的核心配置项与 README 示例一一对应:
id:机器唯一标识,用于可视化与引用;initial:初始状态名;context:可变的扩展状态(extended state),即业务数据;states:状态集合,每个状态可配置:on:事件到转换的映射,{ target: 'active' }或简写'active';entry/exit:进入/离开状态时执行的动作(如assign);invoke:调用子 Actor(见下文 fetch 示例);after:延迟转换(如失败后 1000ms 自动重试);always:无条件/带守卫的瞬时转换。
从源码看,createMachine本质是new StateMachine(config, implementations)(见 createMachine.ts),StateMachine实现了ActorLogic接口,负责转换计算、初始微步进、状态节点解析等,见 StateMachine.ts。
createActor:Actor 选项与生命周期
createActor(logic, options)支持以下选项(见 createActor.ts):
| 选项 | 作用 |
|---|---|
input | 传递给逻辑的输入数据,由logic.getInitialSnapshot(scope, input)消费 |
id | 相对父级的唯一标识,缺省时使用sessionId |
parent | 父 Actor 引用,缺省时创建以自己为根的新 Actor 系统 |
clock | 负责设置/清除延迟事件定时器的时钟,默认使用全局setTimeout/clearTimeout |
logger | 日志函数,默认console.log |
syncSnapshot | 是否将活跃快照同步中继给父级 |
snapshot/state | 用于从持久化快照恢复(rehydration) |
devTools | 是否连接 DevTools 适配器 |
生命周期要点:
- 创建 Actor 会隐式创建一个以该 Actor 为根的系统(
createSystem),任何从根 Actor spawn 出来的后代都属于该系统; - 必须先
start()再send();根 Actor 可以stop(),同时停止整个系统及其所有 Actor; subscribe(observer)返回带unsubscribe()的订阅对象,Actor 停止时所有观察者会被自动退订;- 状态快照可以通过
getSnapshot()同步读取,持久化用getPersistedSnapshot(); - 旧版
interpret()与Interpreter已废弃,直接别名到createActor/Actor(见 createActor.ts)。
setup:集中声明实现,获得最佳类型推导
对于真实应用,推荐用setup()集中声明副作用与类型,再通过.createMachine(...)建模。源码位于 setup.ts,它把actors、actions、guards、delays等实现注册为可被机器按名字引用的实现表。
以 examples/fetch/src/fetchMachine.ts 为例,展示了异步取数的完整建模:
import { assign, fromPromise, setup } from 'xstate'; export const fetchMachine = setup({ types: { context: {} as { name: string; data: { greeting: string } | null; } }, actors: { fetchUser: fromPromise(({ input }: { input: { name: string } }) => getGreeting(input.name) ) } }).createMachine({ initial: 'idle', context: { name: 'World', data: null }, states: { idle: { on: { FETCH: 'loading' } }, loading: { invoke: { src: 'fetchUser', input: ({ context }) => ({ name: context.name }), onDone: { target: 'success', actions: assign({ data: ({ event }) => event.output }) }, onError: 'failure' } }, success: {}, failure: { after: { 1000: 'loading' }, on: { RETRY: 'loading' } } } });这段代码展示了 XState v5 的多个关键能力:fromPromise把异步函数包装为可调用 Actor;invoke.src引用 setup 中声明的 Actor;onDone/onError处理成功与失败;after实现延迟自动重试;assign更新 context。types.context声明让 context 获得完整类型推导。
再看 examples/timer/src/timerMachine.ts,它演示了fromCallback注册定时器 Actor、guard条件转换、always瞬时转换与全局事件:
export const timerMachine = setup({ actors: { ticks: fromCallback(({ sendBack }) => { const interval = setInterval(() => { sendBack({ type: 'TICK' }); }, 1000); return () => clearInterval(interval); }) } }).createMachine({ // types: 声明事件联合类型 context: { seconds: 0 }, initial: 'stopped', states: { stopped: { on: { start: { guard: ({ context }) => context.seconds > 0, // 条件转换 target: 'running' }, minute: { actions: assign({ seconds: ({ context }) => context.seconds + 60 }) } } }, running: { invoke: { src: 'ticks' }, on: { stop: 'stopped', TICK: { actions: assign({ seconds: ({ context }) => context.seconds - 1 }) } }, always: { guard: ({ context }) => context.seconds === 0, // 瞬时转换 target: 'stopped' } } } });核心包导出的 API 还包括and/or/not/stateIn组合守卫、getNextSnapshot、waitFor、toPromise、SimulatedClock、mapState等(见 packages/core/src/index.ts)。
@xstate/store:轻量状态管理
不是每个应用都需要状态机的全部能力。@xstate/store是一个独立、极小的事件驱动 store,TypeScript 推导一流,风格接近 Redux/Zustand 但样板代码更少。可以单独使用,也可以在逻辑复杂后升级到完整的状态机。
npm install @xstate/storeimport { createStore } from '@xstate/store'; const donutStore = createStore({ context: { donuts: 0, favoriteFlavor: 'chocolate' }, on: { addDonut: (context) => ({ ...context, donuts: context.donuts + 1 }), changeFlavor: (context, event: { flavor: string }) => ({ ...context, favoriteFlavor: event.flavor }), eatAllDonuts: (context) => ({ ...context, donuts: 0 }) } }); donutStore.subscribe((snapshot) => { console.log(snapshot.context); }); donutStore.send({ type: 'addDonut' }); // => { donuts: 1, favoriteFlavor: 'chocolate' } donutStore.send({ type: 'changeFlavor', flavor: 'strawberry' }); // => { donuts: 1, favoriteFlavor: 'strawberry' }@xstate/store的完整文档(含 React/Solid 绑定、selector 等)见 packages/xstate-store/README.md,测试与实现位于 packages/xstate-store/src/alien.ts 与 packages/xstate-store/test。
状态机的四种核心形态
README 用"代码 + Statechart 可视化"成对呈现了四种形态,这是 XState 建模的基本功。
1. 有限状态机(Finite State Machine)
三个状态首尾相接,每次TIMER事件推进一档:
import { createMachine, createActor } from 'xstate'; const lightMachine = createMachine({ id: 'light', initial: 'green', states: { green: { on: { TIMER: 'yellow' } }, yellow: { on: { TIMER: 'red' } }, red: { on: { TIMER: 'green' } } } }); const actor = createActor(lightMachine); actor.subscribe((state) => console.log(state.value)); actor.start(); // logs 'green' actor.send({ type: 'TIMER' }); // logs 'yellow'注意这里的on: { TIMER: 'yellow' }是{ target: 'yellow' }的简写语法。
2. 层级(嵌套)状态机(Hierarchical / Nested)
状态内部还可以嵌套子状态。下面例子把行人信号灯嵌套在red中:
import { createMachine, createActor } from 'xstate'; const pedestrianStates = { initial: 'walk', states: { walk: { on: { PED_TIMER: 'wait' } }, wait: { on: { PED_TIMER: 'stop' } }, stop: {} } }; const lightMachine = createMachine({ id: 'light', initial: 'green', states: { green: { on: { TIMER: 'yellow' } }, yellow: { on: { TIMER: 'red' } }, red: { on: { TIMER: 'green' }, ...pedestrianStates } } }); const actor = createActor(lightMachine); actor.subscribe((state) => console.log(state.value)); actor.start(); // logs 'green' actor.send({ type: 'TIMER' }); // logs 'yellow' actor.send({ type: 'TIMER' }); // logs { red: 'walk' } actor.send({ type: 'PED_TIMER' }); // logs { red: 'wait' }进入嵌套状态后,state.value会呈现为对象形态(如{ red: 'walk' }),这正是resolveStateValue等状态解析逻辑(stateUtils.ts)的作用结果。
3. 并行状态机(Parallel State Machines)
把机器声明为type: 'parallel',多个区域(region)同时独立活动,任一区域的转换不影响其他区域:
import { createMachine, createActor } from 'xstate'; const wordMachine = createMachine({ id: 'word', type: 'parallel', states: { bold: { initial: 'off', states: { on: { on: { TOGGLE_BOLD: 'off' } }, off: { on: { TOGGLE_BOLD: 'on' } } } }, underline: { initial: 'off', states: { on: { on: { TOGGLE_UNDERLINE: 'off' } }, off: { on: { TOGGLE_UNDERLINE: 'on' } } } }, italics: { initial: 'off', states: { on: { on: { TOGGLE_ITALICS: 'off' } }, off: { on: { TOGGLE_ITALICS: 'on' } } } }, list: { initial: 'none', states: { none: { on: { BULLETS: 'bullets', NUMBERS: 'numbers' } }, bullets: { on: { NONE: 'none', NUMBERS: 'numbers' } }, numbers: { on: { BULLETS: 'bullets', NONE: 'none' } } } } } }); const actor = createActor(wordMachine); actor.subscribe((state) => console.log(state.value)); actor.start(); // logs { bold: 'off', italics: 'off', underline: 'off', list: 'none' } actor.send({ type: 'TOGGLE_BOLD' }); // logs { bold: 'on', italics: 'off', underline: 'off', list: 'none' } actor.send({ type: 'TOGGLE_ITALICS' }); // logs { bold: 'on', italics: 'on', underline: 'off', list: 'none' }并行状态在 UI 中非常适合表达互不干扰的多个开关/选项组。
4. 历史状态(History States)
type: 'history'的状态用于"记住"离开前的子状态,返回时直接恢复到记忆中的位置:
import { createMachine, createActor } from 'xstate'; const paymentMachine = createMachine({ id: 'payment', initial: 'method', states: { method: { initial: 'cash', states: { cash: { on: { SWITCH_CHECK: 'check' } }, check: { on: { SWITCH_CASH: 'cash' } }, hist: { type: 'history' } }, on: { NEXT: 'review' } }, review: { on: { PREVIOUS: 'method.hist' } } } }); const actor = createActor(paymentMachine); actor.subscribe((state) => console.log(state.value)); actor.start(); // logs { value: { method: 'cash' } } actor.send({ type: 'SWITCH_CHECK' }); // logs { value: { method: 'check' } } actor.send({ type: 'NEXT' }); // logs { value: 'review' } actor.send({ type: 'PREVIOUS' }); // logs { value: { method: 'check' } }注意PREVIOUS: 'method.hist'使用点路径定位到嵌套历史状态,返回后恢复的是check而非初始的cash。
仓库中的更多实战示例
本仓库 examples 目录包含大量可直接运行的示例,覆盖各种技术栈与业务场景,可作为学习与脚手架参考:
- 框架集成:
7guis-counter-react、7guis-temperature-react、friends-list-react、snake-react、tic-tac-toe-react、todomvc-react、tiles、timer等 React 示例,以及7guis-1-counter-vue、7guis-2-temperature-vue等 Vue 示例; - 纯逻辑/后端:
express-workflow(Express 工作流)、mongodb-credit-check-api(MongoDB 信用核查)、mongodb-persisted-state(MongoDB 持久化状态恢复)、fetch(异步请求状态机)、counter、stopwatch、toggle; - 工作流编排:
workflow-*系列展示了事件驱动编排、并行子流程、异步子流程、超时截止、云事件发送等大量后端编排模式。
以 examples/friends-list-react/src/friendsMachine.ts 为代表的 UI 示例展示了机器与组件的集成方式:组件通过useMachine/useActorRef(对应 packages/xstate-react)订阅快照并把事件send回机器。
SemVer 政策与升级注意事项
XState 对公开契约非常谨慎,README 明确了以下承诺:
- 运行时 API:不会在 minor 或 patch 版本中引入破坏性变更;
- 行为变更:由于 XState 会执行大量用户逻辑,任何行为变化都可能被视为破坏性变更,团队会谨慎评估,但保留在 minor 版本中做某些行为调整的权利,升级前务必阅读 release notes;
- TypeScript 类型:团队保留在 minor 版本中调整类型声明或放弃对旧版 TypeScript 支持的权利——TypeScript 本身演进迅速,类型推导也在持续改进;
- 包依赖:XState 家族大部分包声明对
xstate的 peer dependency,新版本会始终把 peer dependency 范围调整到包含最新版xstate。你可以单独升级xstate而不升级@xstate/react;但升级@xstate/react时,强烈建议同步升级xstate。
当前仓库核心包版本为5.32.6(见 packages/core/package.json),并提供了xstate/guards、xstate/actions、xstate/actors、xstate/graph、xstate/dev等子路径导出,方便按需引入。
总结
从 README 出发,结合源码与示例,可以梳理出 XState v5 的完整使用路径:先用@xstate/store处理简单状态,再用createMachine+setup建模复杂逻辑,用createActor实例化运行,通过start/send/subscribe驱动事件闭环;建模时优先考虑层级、并行、历史状态等 Statechart 结构,最后按 SemVer 政策谨慎规划升级。仓库中的 examples 目录是继续深入学习的绝佳资源,packages/xstate-react、packages/xstate-vue 等框架绑定则提供了与主流 UI 库的无缝集成。
- 前端
- 后端
【免费下载链接】xstate
State machines, statecharts, and actors for complex logic
相关推荐
XState v5 核心库完全指南:状态机、状态图与 Actor 模型驱动复杂应用逻辑
XState v5 核心库完全指南:状态机、状态图与 Actor 模型驱动复杂应用逻辑 XState 是一个面向 JavaScript 与 TypeScript
前端后端用 XState v5 实现最小化 Toggle 开关:状态机、Actor 与事件驱动的完整实战
用 XState v5 实现最小化 Toggle 开关:状态机、Actor 与事件驱动的完整实战 导读 examples/toggle 是当前 XState 仓
前端后端@xstate/solid:在 SolidJS 应用中运行 XState 状态机与 Actor 的完整指南
@xstate/solid:在 SolidJS 应用中运行 XState 状态机与 Actor 的完整指南 @xstate/solid 是 XState 官方为
前端后端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考