# @xstate/inspect

@xstate/inspect package (opens new window) 包含 XState 的检查工具。

¥The @xstate/inspect package (opens new window) contains inspection tools for XState.

请参阅此处的 CodeSandbox 示例 (opens new window)

¥See CodeSandbox example here (opens new window)

# 安装

¥Installation

  1. 使用 npm 或 yarn 安装:

    ¥Install with npm or yarn:

npm install @xstate/inspect
# or yarn add @xstate/inspect
  1. 在项目开始时、调用任何其他代码之前导入它:

    ¥Import it at the beginning of your project, before any other code is called:

import { inspect } from '@xstate/inspect';

inspect({
  // options
  // url: 'https://stately.ai/viz?inspect', // (default)
  iframe: false // open in new window
});
  1. { devTools: true } 添加到你想要可视化的任何解释机器中:

    ¥Add { devTools: true } to any interpreted machines you want to visualize:

import { interpret } from 'xstate';
import { inspect } from '@xstate/inspect';
// ...

const service = interpret(someMachine, { devTools: true });

# 检查选项

¥Inspect Options

// defaults
inspect({
  iframe: () => document.querySelector('iframe[data-xstate]'),
  url: 'https://stately.ai/viz?inspect'
});

// the above is the same as this:
inspect();

参数:传递给 inspect(options)options 对象具有以下可选属性:

¥Arguments: the options object passed to inspect(options) with the following optional properties:

  • iframe(函数或 iframe Elementfalse) - 解析为 iframe 元素来显示检查器。如果设置为 iframe: false,则将使用弹出窗口。

    ¥iframe (function or iframe Element or false) - resolves to the iframe element to display the inspector in. If this is set to iframe: false, then a popup window will be used instead.

    ⚠️ 注意:你可能需要允许弹出窗口在弹出窗口中显示检查器,因为默认情况下浏览器可能会阻止它们。

    ¥⚠️ Note: you might need to allow popups to display the inspector in a popup window, as they might be blocked by the browser by default.

    默认情况下,检查器将在文档中的任何位置查找 <iframe data-xstate> 元素。如果你想定位自定义 iframe,请立即指定或延迟指定:

    ¥By default, the inspector will look for an <iframe data-xstate> element anywhere in the document. If you want to target a custom iframe, specify it eagerly or lazily:

    // eager
    inspect({
      iframe: document.querySelector('iframe.some-xstate-iframe')
    });
    
    // lazy
    inspect({
      iframe: () => document.querySelector('iframe.some-xstate-iframe')
    });
    
  • url(字符串) - 要连接的检查器的 URL。默认情况下,检查器在 https://stately.ai/viz?inspect 上运行。

    ¥url (string) - the URL of the inspector to connect to. By default, the inspector is running on https://stately.ai/viz?inspect.

返回:具有以下属性的检查器对象:

¥Returns: an inspector object with the following properties:

  • disconnect(功能) - 断开检查器并清理所有监听器的函数。

    ¥disconnect (function) - a function that disconnects the inspector and cleans up any listeners.

# 实现

¥Implementing

你可以通过创建接收器来实现自己的检查器。接收器是从源(如父窗口或 WebSocket 连接)接收检查器事件的参与者:

¥You can implement your own inspector by creating a receiver. A receiver is an actor that receives inspector events from a source (like a parent window or a WebSocket connection):

  • "service.register"

    {
      type: 'service.register';
      machine: AnyStateMachine;
      state: AnyState;
      id: string;
      sessionId: string;
      parent?: string;
      source?: string;
    }
    
  • "service.stop"

    {
      type: 'service.stop';
      sessionId: string;
    }
    
  • "service.state"

    {
      type: 'service.state';
      state: AnyState;
      sessionId: string;
    }
    
  • "service.event"

    {
      type: 'service.event';
      event: SCXML.Event<any>;
      sessionId: string;
    }
    

要监听来自受检查源的事件,请使用适当的 create*Receiver(...) 函数创建接收器;例如:

¥To listen to events from an inspected source, create a receiver with the appropriate create*Receiver(...) function; for example:

import { createWindowReceiver } from '@xstate/inspect';

const windowReceiver = createWindowReceiver(/* options? */);

windowReceiver.subscribe((event) => {
  // here, you will receive "service.*" events
  console.log(event);
});

你还可以将事件发送到接收者:

¥You can also send events to the receiver:

// ...

// This will send the event to the inspected service
windowReceiver.send({
  type: 'xstate.event',
  event: JSON.stringify({ type: 'someEvent' }),
  service: /* session ID of the service this event is sent to */
});

典型的检验工作流程如下:

¥The typical inspection workflow is as follows:

  1. 客户端上的 inspect(/* ... */) 调用打开检查器(例如,在单独的窗口中,或创建 WebSocket 连接)

    ¥The inspect(/* ... */) call on the client opens the inspector (e.g., in a separate window, or creates a WebSocket connection)

  2. 接收方向客户端发送 "xstate.inspecting" 事件

    ¥The receiver sends an "xstate.inspecting" event to the client

  3. 客户端发送 "service.register" 事件给接收者

    ¥The client sends "service.register" events to the receiver

  4. 监听接收器(通过 receiver.subscribe(...))的检查器通过其 event.sessionId 注册机器(event.machine

    ¥An inspector listening to the receiver (via receiver.subscribe(...)) registers the machine (event.machine) by its event.sessionId

  5. 机器以视觉方式呈现,并高亮其当前状态(event.state

    ¥The machine is visually rendered, and its current state (event.state) is highlighted

  6. 当源处的服务接收事件并改变状态时,它将分别向接收者发送 "service.event""service.state" 事件

    ¥As the service at the source receives events and changes state, it will send the receiver "service.event" and "service.state" events, respectively

  7. 检查器可以使用这些事件来高亮当前状态并保留发送到该服务的事件日志

    ¥The inspector can use those events to highlight the current state and keep a log of events sent to that service

  8. 当服务停止时,会向接收者发送 "service.stop" 事件和 event.sessionId 来标识停止的服务。

    ¥When the service stops, a "service.stop" event is sent to the receiver with the event.sessionId to identify the stopped service.

# 常见问题

¥FAQs

  • 如何在 NextJS 应用中运行检查器?

    ¥How do I run the inspector in a NextJS app?

    确保检查器代码仅在客户端运行,而不是在服务器上运行:

    ¥Ensure that the inspector code only runs on the client, rather than the server:

    if (typeof window !== 'undefined') {
      inspect({
        /* options */
      });
    }