
前端后端【免费下载链接】xstateState machines, statecharts, and actors for complex logic项目地址https://gitcode.com/gh_mirrors/xs/xstate点击查看免费下载导读本文基于本仓库中的 examples/trivia-game-example 示例完整拆解如何用 XStatev5为一款 Rick Morty 角色猜谜 问答游戏设计状态机包括机器 context 的定义、状态与转移的组织方式、守卫guard与动作action的用法、以及如何通过fromPromise接入外部 API 的异步逻辑。读完本文你将能够独立搭建一个具备计分、生命值、线索、胜负判定与重玩循环的完整问答类游戏状态机并理解它与 React 视图层的协作模式。游戏规则与状态机建模目标先看 README 中定义的业务规则这些规则直接决定了机器的 context 字段与状态划分玩家初始拥有 3 条生命、0 分猜对角色 10 分猜错扣 1 条生命每题可查看 1 条线索累计 100 分获胜生命耗尽即失败无论胜负均可重置重玩。对应到状态机层面所有可变数据都会收敛进机器 context所有流程节点都会收敛进状态树。这种数据进 context、流程进状态的划分正是 XState 建模的核心思路。Machine Context机器可访问与修改的全部变量README 强调要让机器工作必须首先为它定义 contextcontext 中装着机器能够访问和修改的所有变量。在本例中context 通过setup({ types: { context: ... } })做类型化声明再在createMachine中给出初始值见 src/triviaMachine.ts 与 src/triviaMachine.tscontext: { homePageCharacters: [], // 首页展示的角色列表 hasLoaded: false, // 数据是否加载完成控制 loading 层 currentCharacter: null, // 当前题目的正确角色 randomCharacters: [], // 3 个干扰项角色 isClueOpened: false, // 线索弹层是否打开 points: 0, // 当前得分 question: 0, // 已答题目计数 lifes: 3 // 剩余生命 }各字段与业务规则的对应关系如下context 字段类型初始值业务含义homePageCharactersArrayRMCharacter[]首页背景图/展示用角色数据hasLoadedbooleanfalse异步加载完成标志驱动加载遮罩显隐currentCharacterRMCharacter \| nullnull当前题目的正确答案来自 APIrandomCharactersArrayRMCharacter[]每题随机生成的干扰选项isClueOpenedbooleanfalse线索气泡是否展开pointsnumber0得分达 100 获胜questionnumber0题目序号加载完成后自增lifesnumber3生命值归零失败角色数据结构定义在 src/common/types.ts包含id、name、status、species、gender、image与episode角色出场剧集 URL 列表用于线索功能。在 XState v5 中setup()是机器定义的统一入口types.context中的类型标注会贯穿后续所有assign动作与守卫的推断让整个机器的状态更新具备端到端的类型安全。状态与转移机器如何动起来README 的核心概念部分指出机器由状态state组成每个状态定义应用当前所处的位置以及该位置可应用的逻辑需要进入其他状态时通过转移transition完成转移同时定义了目的地与来源状态。以 README 中的例子为准初始状态为questionStart接收user.selectAnswer转移事件若答案正确进入correctAnswer否则进入incorrectAnswer。对应源码实现见 src/triviaMachine.tsquestionStart: { on: { user.selectAnswer: [ { target: correctAnswer, guard: isAnswerCorrect }, { target: incorrectAnswer, guard: not(isAnswerCorrect) } ] } }这里使用了 XState 的条件转移conditional transition同一个事件配多个转移分支每个分支挂一个守卫not(isAnswerCorrect)是 XState 内置的守卫取反工具保证两个分支互斥覆盖。完整状态树总览整个机器以homepage为初始状态嵌套结构如下均出自 src/triviaMachine.tshomepage首页loadingData调用loadHomePageCharactersactor加载首页角色数据dataLoaded等待user.play进入规则弹窗instructionModal游戏规则说明弹窗支持user.close/user.reject返回首页user.accept进入游戏startTrivia游戏主流程entry中执行页面跳转与数据重置loadQuestionData加载一题所需的数据loadCharacter调用loadSingleCharacter获取当前题目的正确角色loadRandomCharacters调用loadRandomCharacters生成 3 个干扰选项并让question自增questionReady题目就绪等待玩家作答questionStart等待user.selectAnswercorrectAnswerentry中points 10随后用always判定胜负incorrectAnswerentry中lifes - 1同样用always判定胜负lostGame/wonGame终局状态user.playAgain回到#startTrivia重玩。注意homepage与startTrivia都是复合状态compound state内嵌各自子状态机lostGame/wonGame之间的胜负判定使用了always无事件、进入即判定的瞬态转移这是到达某状态后立刻检查条件的标准写法。守卫guards答题对错与胜负判定守卫是纯函数形式的判定条件在setup({ guards })中注册源码见 src/triviaMachine.tsguards: { isAnswerCorrect: ({ context, event }) { assertEvent(event, user.selectAnswer); if (!context.currentCharacter) return false; return event.answer context.currentCharacter.id; }, hasLostGame: ({ context }) context.lifes 0, hasWonGame: ({ context }) context.points 100 }isAnswerCorrect先通过assertEvent收窄事件类型到user.selectAnswer再比较玩家所选的event.answer与currentCharacter.id是否一致hasLostGame/hasWonGame分别对lifes 0与points 100做判定正是 README 中100 分获胜、生命耗尽失败规则的直接代码化。守卫只负责判断不产生副作用副作用一律交给动作actions完成这是 XState 推荐的设计纪律。动作actions与异步 Actor数据更新与外部 API 接入动作assign更新 context动作用于在转移时修改 context 或执行副作用。本例中注册了页面跳转动作与一个重置动作见 src/triviaMachine.tsactions: { goToTriviaPage: () {}, resetTriviaData: assign({ currentCharacter: null, randomCharacters: [], points: 0, question: 0, lifes: 3 }) }goToTriviaPage在setup阶段为空实现由 React 层通过triviaMachine.provide({ actions: { goToTriviaPage: () navigate(/trivia) } })注入真正的路由跳转逻辑见 src/App.tsx——这是 XState v5 的依赖注入模式机器保持框架无关框架相关副作用由宿主应用提供。得分与扣命的更新同样通过assign完成见 src/triviaMachine.tscorrectAnswer: { entry: assign({ points: ({ context }) context.points 10 }) }, incorrectAnswer: { entry: assign({ lifes: ({ context }) context.lifes - 1 }) }两处都在状态的entry动作中执行即进入该状态的一瞬间完成计分/扣命随后由always瞬态转移判断是否触发终局。异步 ActorfromPromise封装 API 请求游戏所有数据都来自外部 API。XState v5 用fromPromise把 Promise 封装成可被invoke调用的 actor见 src/triviaMachine.tsactors: { loadHomePageCharacters: fromPromise(() RickCharacters.getCharacters(Math.floor(Math.random() * 34)) ), loadSingleCharacter: fromPromise(async () { const randomNumber getRandomNumber(); return RickCharacters.getCharacter(randomNumber); }), loadRandomCharacters: fromPromise(() RickCharacters.getRandomCharacters() ) }API 请求封装在 src/services/RickApi.tsx基于 axios 调用 Rick Morty 公开 APIgetCharacters(page)按页码取一页角色首页展示用getCharacter(id)按 ID 取单个角色当前题目的正确答案getRandomCharacters()通过RICK_AND_MORTY_API/{a},{b},{c}一次取 3 个随机角色干扰选项getClue(episode)按剧集 URL 取线索数据用于 Clue 组件弹层。随机数工具定义在 src/common/constants.tsgetRandomNumber()在0~399区间取整RICK_AND_MORTY_API指向https://rickandmortyapi.com/api/character。actor 通过invoke挂到状态上用onDone接收结果并assign进 context见 src/triviaMachine.tsloadCharacter: { invoke: { src: loadSingleCharacter, onDone: { actions: assign({ currentCharacter: ({ event }) event.output }), target: loadRandomCharacters } } }, loadRandomCharacters: { invoke: { src: loadRandomCharacters, onDone: { actions: assign({ randomCharacters: ({ event }) event.output, question: ({ context }) context.question 1, hasLoaded: true }), target: #questionReady } } }请求失败等异常场景可通过onError分支处理本例中hasLoaded在数据到达后置为true驱动前端LoadingOverlay的显隐。React 视图层Actor Context 与组件订阅机器与视图的桥接通过xstate/react的createActorContext完成见 src/context/AppContext.tsximport { createActorContext } from xstate/react; import triviaMachine from ../triviaMachine; export const TriviaMachineContext createActorContext(triviaMachine);在 src/App.tsx 中TriviaMachineContext.Provider包裹Routes并将provide注入后的机器逻辑传给 provider同时定义了/首页与/trivia游戏页两条路由。视图组件通过三个 hook 与机器协作以 src/components/game/GamePanel.tsx 与 src/components/game/GuessOptions.tsx 为例TriviaMachineContext.useSelector((state) ...)选择订阅状态快照。GamePanel 用它读取完整state并通过state.matches({ startTrivia: { questionReady: lostGame } })这类状态匹配判断是否渲染 Lose / Win 组件TriviaMachineContext.useActorRef()拿到 actor 引用用于发送事件例如triviaActorRef.send({ type: user.selectAnswer, answer: character.id })与send({ type: user.nextQuestion })题目选项由randomCharacters过滤掉正确角色后用 Ramda 的sortBy(prop(id))排序并与currentCharacter合并生成 4 个选项。线索组件 src/components/game/Clue.tsx 读取context.isClueOpened控制 Bootstrap Popover 的显隐点击按钮向机器发送user.toggleClue由机器assign反转isClueOpened——组件只负责展示与发事件所有状态变更都收敛在机器内部这正是 XState 架构的价值所在。安装与本地运行按照 README 的指引先安装依赖再启动开发服务器pnpm installyarn dev开发服务器默认运行在http://localhost:5173。示例还提供其他脚本见 examples/trivia-game-example/package.jsonyarn buildtsc vite build产出生产构建、yarn lintESLint 检查、yarn preview预览生产构建。技术栈速览React 18 TypeScript 5 Vite 5状态机依赖xstate^5.28.0与xstate/react^4.1.3UI 基于 React-Bootstrap 与 styled-components样式使用 Tailwind 与 SCSS数据请求走 axios。小结这套状态机设计带来的工程收益回顾整个 examples/trivia-game-example 示例可以提炼出几条可直接复用的设计经验规则即状态计分、扣命、胜负判定全部落在机器内守卫 always视图层只做渲染与发事件业务逻辑可独立测试、独立演进异步收敛进 actorAPI 加载用fromPromiseinvokeonDone编排hasLoaded标志统一驱动加载态杜绝组件内散落的 loading 分支副作用依赖注入路由跳转等框架相关副作用通过provide注入机器本身保持框架无关、可移植类型安全贯穿setup的types声明让事件、context、守卫、动作全程获得 TypeScript 推断重构时错误在编译期暴露。如果你希望查看完整机器定义与全部组件实现可直接阅读 src/triviaMachine.ts、src/context/AppContext.tsx 与 src/components/game 目录下的各组件XState 核心包源码位于 packages/core/src可进一步研究setup、fromPromise、assign、not等 API 的底层实现。赞分享前端后端【免费下载链接】xstateState machines, statecharts, and actors for complex logic项目地址https://gitcode.com/gh_mirrors/xs/xstate点击查看免费下载相关推荐XState游戏开发终极指南如何用状态机设计复杂游戏逻辑XState游戏开发终极指南如何用状态机设计复杂游戏逻辑 游戏开发中复杂的逻辑状态管理常常让开发者头疼不已。XState作为一款强大的状态管理库通过状态机前端后端CANN pyasc 数据搬运接口 asc.data_copy_pad 详解非对齐搬运与填充实战CANN pyasc 数据搬运接口 asc.data_copy_pad 详解非对齐搬运与填充实战 导读 asc.data_copy_pad 是 CANN py前端后端XState实战构建复杂的应用状态管理XState实战构建复杂的应用状态管理 本文深入探讨了XState状态管理库的核心概念和高级用法重点介绍了createMachine API的配置选项、上下前端后端上一篇解决ESP32音频播放崩溃深度剖析I2S项目内存管理优化方案下一篇OBS Source Record插件如何为体育训练视频添加章节标记创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考