Миграция с XState v5
vue-state-machine совместим по API с полезным подмножеством XState v5. Миграция простой машины обычно занимает считаные минуты.
Соответствие API
| XState v5 | vue-state-machine | Заметки |
|---|---|---|
createMachine(config) | defineMachine(config) | Структура конфига идентична |
useMachine(machine) из @xstate/vue | useMachine(config) | Та же форма composable |
send(event) | send(event) | Идентично |
matches(state) | matches(state) | Идентично |
context в конфиге | context в конфиге | Идентично |
Обработчики on | Обработчики on | Идентично |
entry / exit | entry / exit | Идентично |
type: 'final' | type: 'final' | Идентично |
Функция guard | Функция guard | Та же сигнатура |
assign(updater) | Возврат Partial<context> из action | Обёртка не нужна |
snapshot / restore | snapshot / restore | Та же концепция |
invoke / сервисы | Не поддерживается | Перенесите асинхронную работу в actions |
spawn / модель акторов | Не поддерживается | Осознанное ограничение области |
| Иерархические состояния | Не поддерживается | Только плоские + параллельные |
Пошаговая миграция
1. Замените импорт и фабрику:
ts
// До (XState v5)
import { createMachine } from 'xstate'
const machine = createMachine({ ... })
// После
import { defineMachine } from 'vue-state-machine'
const machine = defineMachine({ ... })2. Замените assign() на обычные возвращаемые значения:
ts
// До
import { assign } from 'xstate'
const increment = assign({ count: (ctx) => ctx.count + 1 })
// После — просто верните частичный объект контекста
const increment = (ctx: { count: number }) => ({ count: ctx.count + 1 })3. Замените импорт Vue-composable:
ts
// До
import { useMachine } from '@xstate/vue'
// После
import { useMachine } from 'vue-state-machine'4. Перенесите асинхронную логику из invoke в actions:
ts
// До (XState v5 invoke)
loading: {
invoke: {
src: (ctx, event) => fetch('/api/user'),
onDone: { target: 'success', actions: assign({ user: (_, e) => e.data }) },
onError: { target: 'error' },
}
}
// После — fire-and-forget внутри компонента или внутри entry action
loading: {
entry: [async (ctx, event) => {
try {
const user = await fetch('/api/user').then(r => r.json())
return { user } // сливается в контекст; затем SUCCESS отправляется извне
} catch {
return { error: 'Failed' }
}
}]
}