Skip to content

vue-state-machine

Лёгкие реактивные конечные автоматы (FSM / statechart) для Vue 3 — декларативные состояния и переходы, параллельные регионы, guards, actions, персистентность и composable-API — с единственной peer-зависимостью.

Возможности

  • defineMachine() — чистая фабрика конфигурации с валидацией в dev-режиме; без зависимости от Vue — тестируется в Node
  • useMachine() — composable, оборачивающий машину в реактивность Vue; реактивные state, context, send(), matches(), can()
  • Guards — синхронные предикаты, блокирующие переходы; исключение трактуется как false
  • Actions — синхронные или асинхронные побочные эффекты на входе, выходе или переходе; возвращают Partial<context> для обновления состояния
  • Очередь событийsend() добавляет в очередь и обрабатывает события последовательно; никаких состояний гонки с асинхронными actions
  • Параллельные регионы — несколько независимых под-машин, активных одновременно внутри состояния
  • useWizard() — построен поверх useMachine; next(), prev(), goTo(), асинхронный canProceed, хуки onEnter/onLeave, циклический режим
  • Персистентность — опциональная сериализация снапшота в localStorage (или любое кастомное Storage) для каждого инстанса машины
  • История переходов — настраиваемая глубина, полезно для отладки и сценариев отмены
  • useSharedMachine() — singleton-машина, общая для несвязанных компонентов без Pinia
  • DevTools — отдельная точка входа /devtools; кастомная панель в Vue DevTools с состоянием, контекстом, историей и отправкой событий
  • Полный TypeScript — дженерики TState, TEvent, TContext выводятся автоматически из конфига
  • Совместимое подмножество XState v5 — миграция заменой createMachinedefineMachine и assign() → обычное возвращаемое значение
  • SSR-безопасность — никаких window / localStorage в ядре; персистентность молча пропускается на сервере
  • ≤ 4 КБ gzip для ядра (defineMachine + useMachine)

Установка

bash
npm install @macrulez/vue-state-machine

Peer-зависимость:

bash
npm install vue@>=3.3

Быстрый старт

vue
<script setup lang="ts">
import { defineMachine, useMachine } from 'vue-state-machine'

const trafficLight = defineMachine({
  id: 'traffic',
  initial: 'red',
  states: {
    red: { on: { NEXT: { target: 'green' } } },
    green: { on: { NEXT: { target: 'yellow' } } },
    yellow: { on: { NEXT: { target: 'red' } } },
  },
})

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

<template>
  <div :class="state">
    <p>Current: {{ state }}</p>
    <button @click="send('NEXT')">Next</button>
  </div>
</template>

state — реактивный Ref<'red' | 'green' | 'yellow'>. Клик по кнопке переводит машину в новое состояние, и Vue перерисовывает интерфейс автоматически.