XState 做为一个非常好用的前端状态机库,但官网文档却只有英文版,为了阅读体验,我这里翻译了中文版。
- 仓库地址:https://github.com/lecepin/xstate-docs-cn
- 文档地址:https://lecepin.github.io/xstate-docs-cn/zh
由于本人水平有限,加上翻译周期较短,出现错误在所难免,欢迎广大读者批评指正。
JavaScript 状态机和状态图
用于现代 Web 的 JavaScript 和 TypeScript 的 有限状态机 和 状态图 。
还不了解状态机和状态图? 阅读我们的介绍。
📑 遵守 SCXML 规范
💬 在 Stately Discord Community 和我们交流
包
- 🤖
xstate
- 有限状态机和状态图核心库 + 解释器 - 🔬
@xstate/fsm
- 最小有限状态机库 - 📉
@xstate/graph
- XState 的图遍历实用工具包 - ⚛️
@xstate/react
- 在 React 应用中使用 XState 的 React Hooks 和实用工具包 - 💚
@xstate/vue
- 用于在 Vue 应用中使用 XState 的 Vue 组合函数和实用工具包 - 🎷
@xstate/svelte
- 用于在 Svelte 应用中使用 XState 的 Svelte 实用工具包 - ✅
@xstate/test
- 基于模型测试的实用工具包(使用 XState) - 🔍
@xstate/inspect
- XState 的检查实用工具包
模板
首先在 CodeSandbox 上创建这些模板之一:
- XState Template - 没有框架
- XState + TypeScript Template - 没有框架
- XState + React Template
- XState + React + TypeScript Template
- XState + Vue Template
- XState + Vue 3 Template
- XState + Svelte Template
超级快速上手
npm install xstate
import { createMachine, interpret } from 'xstate';// 无状态的状态机定义
// machine.transition(...) 是解释器使用的纯函数。
const toggleMachine = createMachine({id: 'toggle',initial: 'inactive',states: {inactive: {on: {TOGGLE: { target: 'active' }}},active: {on: {TOGGLE: { target: 'inactive' }}}}
});// 具有内部状态的状态机实例
const toggleService = interpret(toggleMachine).onTransition((state) => console.log(state.value)).start();
// => 'inactive'toggleService.send({ type: 'TOGGLE' });
// => 'active'toggleService.send({ type: 'TOGGLE' });
// => 'inactive'
Promise 示例
📉 在 stately.ai/viz 上查看可视化
import { createMachine, interpret, assign } from 'xstate';const fetchMachine = createMachine({id: 'Dog API',initial: 'idle',context: {dog: null},states: {idle: {on: {FETCH: { target: 'loading' }}},loading: {invoke: {id: 'fetchDog',src: (context, event) =>fetch('https://dog.ceo/api/breeds/image/random').then((data) =>data.json()),onDone: {target: 'resolved',actions: assign({dog: (_, event) => event.data})},onError: {target: 'rejected'}},on: {CANCEL: { target: 'idle' }}},rejected: {on: {FETCH: { target: 'loading' }}},resolved: {type: 'final'}}
});const dogService = interpret(fetchMachine).onTransition((state) => console.log(state.value)).start();dogService.send({ type: 'FETCH' });
- 可视化工具
- 为什么?
- 有限状态机
- 分层(嵌套)状态机
- 并行状态机
- 历史状态
可视化工具
在 XState Viz 中可视化、模拟和共享你的状态图!
为什么?
状态图是一种,用于对有状态的交互式系统,进行建模的方式。从单个组件到整个应用程序逻辑,这对于以声明方式描述应用的 行为 非常有用。
阅读 📽 幻灯片 (🎥 视频) 或查看这些资源以了解有限状态机和状态图在 UI 中的重要性:
- 状态图 - 一个复杂系统的可视化表现 by David Harel
- 状态图的世界 by Erik Mogensen
- 纯 UI by Guillermo Rauch
- 纯 UI 控制 by Adam Solove
- Spectrum - 状态图社区 (对于 XState 特定问题,请使用 GitHub 讨论)
有限状态机
import { createMachine } from 'xstate';const lightMachine = createMachine({id: 'light',initial: 'green',states: {green: {on: {TIMER: { target: 'yellow' }}},yellow: {on: {TIMER: { target: 'red' }}},red: {on: {TIMER: { target: 'green' }}}}
});const currentState = 'green';const nextState = lightMachine.transition(currentState, { type: 'TIMER' }).value;// => 'yellow'
分层(嵌套)状态机
import { createMachine } from 'xstate';const pedestrianStates = {initial: 'walk',states: {walk: {on: {PED_TIMER: { target: 'wait' }}},wait: {on: {PED_TIMER: { target: 'stop' }}},stop: {}}
};const lightMachine = createMachine({id: 'light',initial: 'green',states: {green: {on: {TIMER: { target: 'yellow' }}},yellow: {on: {TIMER: { target: 'red' }}},red: {on: {TIMER: { target: 'green' }},...pedestrianStates}}
});const currentState = 'yellow';const nextState = lightMachine.transition(currentState, { type: 'TIMER' }).value;
// => {
// red: 'walk'
// }lightMachine.transition('red.walk', { type: 'PED_TIMER' }).value;
// => {
// red: 'wait'
// }
分层状态的对象符号:
// ...
const waitState = lightMachine.transition({ red: 'walk' },{ type: 'PED_TIMER' }
).value;// => { red: 'wait' }lightMachine.transition(waitState, { type: 'PED_TIMER' }).value;// => { red: 'stop' }lightMachine.transition({ red: 'stop' }, { type: 'TIMER' }).value;// => 'green'
并行状态机
import { createMachine } from 'xstate';const wordMachine = createMachine({id: 'word',type: 'parallel',states: {bold: {initial: 'off',states: {on: {on: {TOGGLE_BOLD: { target: 'off' }}},off: {on: {TOGGLE_BOLD: { target: 'on' }}}}},underline: {initial: 'off',states: {on: {on: {TOGGLE_UNDERLINE: { target: 'off' }}},off: {on: {TOGGLE_UNDERLINE: { target: 'on' }}}}},italics: {initial: 'off',states: {on: {on: {TOGGLE_ITALICS: { target: 'off' }}},off: {on: {TOGGLE_ITALICS: { target: 'on' }}}}},list: {initial: 'none',states: {none: {on: {BULLETS: { target: 'bullets' },NUMBERS: { target: 'numbers' }}},bullets: {on: {NONE: { target: 'none' },NUMBERS: { target: 'numbers' }}},numbers: {on: {BULLETS: { target: 'bullets' },NONE: { target: 'none' }}}}}}
});const boldState = wordMachine.transition('bold.off', { type: 'TOGGLE_BOLD' }).value;// {
// bold: 'on',
// italics: 'off',
// underline: 'off',
// list: 'none'
// }const nextState = wordMachine.transition({bold: 'off',italics: 'off',underline: 'on',list: 'bullets'},{ type: 'TOGGLE_ITALICS' }
).value;// {
// bold: 'off',
// italics: 'on',
// underline: 'on',
// list: 'bullets'
// }
历史状态
import { createMachine } from 'xstate';const paymentMachine = createMachine({id: 'payment',initial: 'method',states: {method: {initial: 'cash',states: {cash: {on: {SWITCH_CHECK: { target: 'check' }}},check: {on: {SWITCH_CASH: { target: 'cash' }}},hist: { type: 'history' }},on: {NEXT: { target: 'review' }}},review: {on: {PREVIOUS: { target: 'method.hist' }}}}
});const checkState = paymentMachine.transition('method.cash', {type: 'SWITCH_CHECK'
});// => State {
// value: { method: 'check' },
// history: State { ... }
// }const reviewState = paymentMachine.transition(checkState, { type: 'NEXT' });// => State {
// value: 'review',
// history: State { ... }
// }const previousState = paymentMachine.transition(reviewState, {type: 'PREVIOUS'
}).value;// => { method: 'check' }