vue-state-machine
Лёгкие реактивные конечные автоматы (FSM / statechart) для Vue 3 — декларативные состояния и переходы, параллельные регионы, guards, actions, персистентность и composable-API — с единственной peer-зависимостью.
Возможности
defineMachine()— чистая фабрика конфигурации с валидацией в dev-режиме; без зависимости от Vue — тестируется в NodeuseMachine()— 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 — миграция заменой
createMachine→defineMachineиassign()→ обычное возвращаемое значение - SSR-безопасность — никаких
window/localStorageв ядре; персистентность молча пропускается на сервере - ≤ 4 КБ gzip для ядра (
defineMachine+useMachine)
Установка
bash
npm install @macrulez/vue-state-machinePeer-зависимость:
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 перерисовывает интерфейс автоматически.