# @xstate/vue

@xstate/vue package (opens new window) 包含将 XState (opens new window)Vue (opens new window) 结合使用的实用程序。

¥The @xstate/vue package (opens new window) contains utilities for using XState (opens new window) with Vue (opens new window).

[[目录]]

¥

Vue 2 注意:

如果你使用的是 Vue 2.x,请参阅 Vue 秘诀;如果你想使用 Vue Composition API,请使用 xstate-vue2 (opens new window)

¥If you're using Vue 2.x, please see the Vue recipe instead, or use the xstate-vue2 package (opens new window) if you want to use the Vue Composition API.

# 快速开始

¥Quick Start

  1. 安装 xstate@xstate/vue

    ¥Install xstate and @xstate/vue:

npm i xstate @xstate/vue

通过 CDN

¥Via CDN

<script src="https://unpkg.com/@xstate/vue/dist/xstate-vue.min.js"></script>

通过使用全局变量 XStateVue

¥By using the global variable XStateVue

  1. 导入 useMachine 合成函数:

    ¥Import the useMachine composition function:

<script setup>
import { useMachine } from '@xstate/vue';
import { createMachine } from 'xstate';

const toggleMachine = createMachine({
  id: 'toggle',
  initial: 'inactive',
  states: {
    inactive: {
      on: { TOGGLE: 'active' }
    },
    active: {
      on: { TOGGLE: 'inactive' }
    }
  }
});

const { state, send } = useMachine(toggleMachine);
</script>

<template>
  <button @click="send('TOGGLE')">
    {{
      state.value === 'inactive'
        ? 'Click to activate'
        : 'Active! Click to deactivate'
    }}
  </button>
</template>

# API

# useMachine(machine, options?)

vue 组合函数 (opens new window) 解释给定的 machine 并启动在组件的生命周期内运行的服务。

¥A Vue composition function (opens new window) that interprets the given machine and starts a service that runs for the lifetime of the component.

参数

¥Arguments

返回 { state, send, service}

¥Returns { state, send, service}:

  • state - 将机器的当前状态表示为 XState State 对象。

    ¥state - Represents the current state of the machine as an XState State object.

  • send - 将事件发送到正在运行的服务的函数。

    ¥send - A function that sends events to the running service.

  • service - 创建的服务。

    ¥service - The created service.

# useActor(actor, getSnapshot)

提供对现有 actor (opens new window) 的访问的 vue 组合函数 (opens new window)

¥A Vue composition function (opens new window) that provides access to an existing actor (opens new window).

从 0.5.0 开始

¥Since 0.5.0

参数

¥Arguments

  • actor - 一个类似 actor 的对象,包含 .send(...).subscribe(...) 方法。

    ¥actor - an actor-like object that contains .send(...) and .subscribe(...) methods.

  • getSnapshot - 一个应该从 actor 返回最新发出值的函数。

    ¥getSnapshot - a function that should return the latest emitted value from the actor.

    • 默认尝试从 actor.getSnapshot() 获取快照,如果不存在则返回 undefined

      ¥Defaults to attempting to get the snapshot from actor.getSnapshot(), or returning undefined if that does not exist.

import { useActor } from '@xstate/vue';

const props = defineProps(['someSpawnedActor']);

const { state, send } = useActor(props.someSpawnedActor);

要订阅 actor 上的更改,同时保留 props 或其他反应变量的反应性,可以使用 Vue 的 computed (opens new window)

¥To subscribe to changes on the an actor whilst retaining reactivity from props or another reactive variable, Vue's computed (opens new window) can be used.

const { state, send } = useActor(computed(() => props.someSpawnedActor));

# useInterpret(machine, options?, observer?)

vue 组合函数 (opens new window) 返回从 machineoptions 创建的 service(如果指定)。它还设置了对 serviceobserver 的订阅(如果提供)。

¥A Vue composition function (opens new window) that returns the service created from the machine with the options, if specified. It also sets up a subscription to the service with the observer, if provided.

从 0.5.0 开始

¥Since 0.5.0

参数

¥Arguments

  • machine - XState 机器 (opens new window) 或延迟返回机器的函数。

    ¥machine - An XState machine (opens new window) or a function that lazily returns a machine.

  • options(可选) - 解释器选项 (opens new window) 和/或以下任何机器配置选项:guardsactionsservicesdelaysimmediatecontextstate

    ¥options (optional) - Interpreter options (opens new window) and/or any of the following machine config options: guards, actions, services, delays, immediate, context, state.

  • observer(可选) - 监听状态更新的观察者或监听器:

    ¥observer (optional) - an observer or listener that listens to state updates:

    • 观察员(例如 { next: (state) => {/* ... */} }

      ¥an observer (e.g., { next: (state) => {/* ... */} })

    • 或监听器(例如,(state) => {/* ... */}

      ¥or a listener (e.g., (state) => {/* ... */})

import { useInterpret } from '@xstate/vue';
import { someMachine } from '../path/to/someMachine';

const service = useInterpret(someMachine);

使用选项+监听器:

¥With options + listener:

import { useInterpret } from '@xstate/vue';
import { someMachine } from '../path/to/someMachine';

const service = useInterpret(
  someMachine,
  {
    actions: {
      /* ... */
    }
  },
  (state) => {
    // subscribes to state changes
    console.log(state.value);
  }
);

# useSelector(actor, selector, compare?, getSnapshot?)

vue 组合函数 (opens new window)actor 的快照返回选定的值,例如服务。仅当所选值发生更改(由可选的 compare 函数确定)时,此钩子才会导致重新渲染。

¥A Vue composition function (opens new window) that returns the selected value from the snapshot of an actor, such as a service. This hook will only cause a rerender if the selected value changes, as determined by the optional compare function.

从 0.6.0 开始

¥Since 0.6.0

参数

¥Arguments

  • actor - 包含 .send(...).subscribe(...) 方法的服务或类似参与者的对象。

    ¥actor - a service or an actor-like object that contains .send(...) and .subscribe(...) methods.

  • selector - 一个函数,它将角色的 "当前状态"(快照)作为参数并返回所需的选定值。

    ¥selector - a function that takes in an actor's "current state" (snapshot) as an argument and returns the desired selected value.

  • compare(可选) - 确定当前选择的值是否与先前选择的值相同的函数。

    ¥compare (optional) - a function that determines if the current selected value is the same as the previous selected value.

  • getSnapshot(可选) - 一个应该从 actor 返回最新发出值的函数。

    ¥getSnapshot (optional) - a function that should return the latest emitted value from the actor.

    • 默认尝试获取 actor.state,如果不存在则返回 undefined。会自动从服务中提取状态。

      ¥Defaults to attempting to get the actor.state, or returning undefined if that does not exist. Will automatically pull the state from services.

import { useSelector } from '@xstate/vue';

const props = defineProps(['service']);

const selectCount = (state) => state.context.count;

const count = useSelector(props.service, selectCount);

具有 compare 功能:

¥With compare function:

import { useSelector } from '@xstate/vue';

const props = defineProps(['service']);

const selectUser = (state) => state.context.user;
const compareUser = (prevUser, nextUser) => prevUser.id === nextUser.id;

const user = useSelector(props.service, selectUser, compareUser);

对于 useInterpret(...)

¥With useInterpret(...):

import { useInterpret, useSelector } from '@xstate/vue';
import { someMachine } from '../path/to/someMachine';

const selectCount = (state) => state.context.count;

const service = useInterpret(someMachine);
const count = useSelector(service, selectCount);

# 配置机器

¥Configuring Machines

可以通过将机器选项作为 useMachine(machine, options) 的第二个参数传递来配置现有机器。

¥Existing machines can be configured by passing the machine options as the 2nd argument of useMachine(machine, options).

示例:'fetchData' 服务和 'notifySuccess' 操作都是可配置的:

¥Example: the 'fetchData' service and 'notifySuccess' action are both configurable:

<script setup>
import { assign, createMachine } from 'xstate';
import { useMachine } from '@xstate/vue';

const fetchMachine = createMachine({
  id: 'fetch',
  initial: 'idle',
  context: {
    data: undefined,
    error: undefined
  },
  states: {
    idle: {
      on: { FETCH: 'loading' }
    },
    loading: {
      invoke: {
        src: 'fetchData',
        onDone: {
          target: 'success',
          actions: assign({
            data: (_context, event) => event.data
          })
        },
        onError: {
          target: 'failure',
          actions: assign({
            error: (_context, event) => event.data
          })
        }
      }
    },
    success: {
      entry: 'notifySuccess',
      type: 'final'
    },
    failure: {
      on: {
        RETRY: 'loading'
      }
    }
  }
});

const props = defineProps({
  onResolve: {
    type: Function,
    default: () => {}
  }
});

const { state, send } = useMachine(fetchMachine, {
  actions: {
    notifySuccess: (ctx) => props.onResolve(ctx.data)
  },
  services: {
    fetchData: (_context, event) =>
      fetch(`some/api/${event.query}`).then((res) => res.json())
  }
});
</script>

<template>
  <template v-if="state.value === 'idle'">
    <button @click="send({ type: 'FETCH', query: 'something' })">
      Search for something
    </button>
  </template>

  <template v-else-if="state.value === 'loading'">
    <div>Searching...</div>
  </template>

  <template v-else-if="state.value === 'success'">
    <div>Success! {{ state.context.data }}</div>
  </template>

  <template v-else-if="state.value === 'failure'">
    <p>{{ state.context.error.message }}</p>
    <button @click="send('RETRY')">Retry</button>
  </template>
</template>

# 匹配状态

¥Matching States

对于 hierarchical (opens new window)parallel (opens new window) 机器,状态值将是对象,而不是字符串。在这种情况下,最好使用 state.matches(...) (opens new window)

¥For hierarchical (opens new window) and parallel (opens new window) machines, the state values will be objects, not strings. In this case, it's better to use state.matches(...) (opens new window):

<template>
  <div>
    <loader-idle v-if="state.matches('idle')" />
    <loader-loading-user v-else-if="state.matches({ loading: 'user' })" />
    <loader-loading-friends v-else-if="state.matches({ loading: 'friends' })" />
  </div>
</template>

# 持续和再水化状态

¥Persisted and Rehydrated State

你可以通过 options.state 使用 useMachine(...) 保持并补充状态:

¥You can persist and rehydrate state with useMachine(...) via options.state:

<script setup>
// Get the persisted state config object from somewhere, e.g. localStorage
const persistedState = JSON.parse(
  localStorage.getItem('some-persisted-state-key')
);

const { state, send } = useMachine(someMachine, {
  state: persistedState
});
</script>

# 从 0.4.0 迁移

¥Migration from 0.4.0

  • 对于使用 invokespawn(...) 创建的生成 Actor,请使用 useActor() 钩子而不是 useService()

    ¥For spawned actors created using invoke or spawn(...), use the useActor() hook instead of useService():

    -import { useService } from '@xstate/vue';
    +import { useActor } from '@xstate/vue';
    
    -const {state, send} = useService(someActor);
    +const {state, send} = useActor(someActor);