状态图的好处之一是,在将状态图组合在一起的过程中,您可以探索流程中所有可能的状态。这种探索将帮助您避免代码中的错误和错误,因为您更有可能涵盖所有可能发生的情况。
因为状态图是可执行的,所以它们既可以表现为图,也可以表现为代码,这样就不太可能在图环境和编码环境之间引入差异或错误解释。
原理解析
状态图iframe展示页面是xstate官方提供的未开源,基本原理就是通过receptor监听发来的消息,进行状态图绘制。
xstate inspect检查器代理生成状态图:
Xstate提供俩中模式:postMessage或ws 。 进行inspect检查器和iframe内receptor的通讯
状态图页面是xstate官方提供的未开源,基本原理就是通过receptor监听发来的消息,进行状态图重新绘制。
Iframe中receptor接受到传来的序列化消息后,只用基本信息数据做状态图绘制和节点高亮,不涉及逻辑执行。逻辑执行还是由xstate中service执行,执行完转换state后传递state给状态图做高亮。点击状态图的事件节点也是触发service.send()方法让xstate执行send事件。
向iframe中发送消息:iframe.window.postMessage({ type: "xxx", data: {} });
监听postMessage的消息事件:window.addEventListener("message", (e) => { console.log(e.data.type); } });
一。postMessage模式:源码文件browser.ts
1.执行inspect()
1-1.执行inspect()创建了一个检查器inspector(如下inspect函数代码)。创建了一个InspectMachine,创建了一个devTools复制给全局变量:getGlobal().__xstate__ = devTools。
InspectMachine监听响应的xstate.event等事件调用iframe.postMessage触发状态图的更新。 devTools维护listener(主要是代理service的send subsribe onStop)。)和service集合。
1-2.渲染iframe.src https://stately.ai/viz?inspect , 此iframe由xstate官方创建了receptor接受postMesage消息进行对应的视图重新渲染。同时会在receptor创建完成后向parent.window发出'xstate.inspect'消息,parent会将devTools.services全部发送给iframe进行'xstate.register'注册和初始化。
2.然后创建开启devTools的service,interpret(machine,{devTools:true});
2-1.开启了devTools的service start启动时会获取到devTools(getGlobal().__xstate__),
2-2.将service注册register到devTools中(注册时会执行listener(service),代理service的send subsribe onStop,同时会向InspectMachine发出'xstate.register'事件)。
之后service状态再改变,会自动走代理方法InspectMachine.send()触发iframe状态图更新。
二。Ws模式:源码文件server.ts
inspect(); //ws server端,接收iframe的消息,发送给inspectMachine处理【同时发送事件到iframe中(通过ws send),进行状态机初始化 渲染状态图】
创建devTools,并注册listener对service进行代理,send subscribe stop时自动通过ws session 发送事件到iframe中。
渲染iframe src。
iframe中链接到inspect(ws server),作为client发送'xstate.inspecting'初始化完成事件,然后接受inspect发来的ws消息'xstate.register',进行注册状态机初始化 渲染状态图。
interpret(machine,{devTools:true}); 开启dev模式后,通过global.__xstate__将machine注册到devTools中(注册时会执行listener(machine),对machine进行代理,send subscribe stop时自动通过ws session 发送事件到iframe中的ws client端)。
Iframe中receptor接受到传来的序列化消息后,只用基本信息数据做状态图绘制和节点高亮,不涉及逻辑执行。逻辑执行还是由xstate中service执行,执行完转换state后传递state给状态图做高亮。点击状态图的事件节点也是触发service.send()方法让xstate执行send事件。
{
type: 'service.register',
machine: stringifyMachine(service.machine), //
state: stringifyState(state),
sessionId: service.sessionId,
}
{
type: 'service.event',
event: stringifyWithSerializer(state._event),
sessionId: service.sessionId
}
{
type: 'service.state',
// TODO: investigate usage of structuredClone in browsers if available
state: stringifyState(state, options?.serialize),
sessionId: service.sessionId
}
通过inspect可视化绘制状态图
简单演示使用(在前端直接启动的状态机):
ps:正常还是建议在后端启动状态机
<template>
<iframe onload="this.height=document.body.scrollHeight-30" width="100%" scrolling="no" frameborder="0" data-xstate></iframe>
</template>
<script>
import { inspect } from "@xstate/inspect";
async mounted() {
this.subMqtt();
this.init(); // 此处为启动状态机逻辑: interpret(deviceMachine, {devTools:true}).start();
inspect({
iframe: () => document.querySelector("iframe[data-xstate]"),
});
},
}