Определение машин
defineMachine
Чистая фабричная функция. Валидирует конфиг и возвращает его с улучшенными типами TypeScript. Ноль зависимости от Vue — можно вызывать и тестировать в Node без приложения Vue.
ts
function defineMachine<TState, TEvent, TContext>(
config: MachineConfig<TState, TEvent, TContext>,
): MachineConfig<TState, TEvent, TContext>Структура конфига
ts
const machine = defineMachine({
id: 'login', // уникальный идентификатор (обязателен, используется DevTools и MachineStore)
initial: 'idle', // начальное состояние
context: {
// опциональный начальный контекст (глубоко клонируется для каждого инстанса)
attempts: 0,
error: null as string | null,
},
states: {
idle: {
on: {
// имя события → конфиг перехода
SUBMIT: { target: 'loading', actions: [resetError] },
},
},
loading: {
on: {
SUCCESS: { target: 'success' },
FAILURE: { target: 'error', actions: [incrementAttempts] },
},
},
error: {
on: {
RETRY: { target: 'idle', guard: canRetry },
},
},
success: { type: 'final' }, // терминальное — дальнейшие вызовы send() ничего не делают
},
})Опции StateConfig
| Поле | Тип | Описание |
|---|---|---|
on | Record<TEvent, TransitionConfig> | Обработчики событий |
entry | Action[] | Вызывается при входе машины в это состояние |
exit | Action[] | Вызывается при выходе машины из этого состояния |
type | 'final' | Терминальное состояние — isDone становится true, send() игнорируется |
parallel | Record<string, SubMachineConfig> | Параллельные регионы (см. Параллельные состояния) |
Опции TransitionConfig
| Поле | Тип | Описание |
|---|---|---|
target | TState | Целевое состояние (проверяется TypeScript'ом против конфига) |
guard | Guard<TContext, TEvent> | Синхронный предикат; false или выброшенное исключение блокирует переход |
actions | Action<TContext, TEvent>[] | Побочные эффекты, выполняемые во время перехода |
Валидация в dev-режиме
В режиме разработки (import.meta.env.DEV !== false) defineMachine выбрасывает содержательные ошибки для:
- Пустого
config.id initial, не найденного вstates- Любого
target, ссылающегося на несуществующее состояние
Валидация удаляется tree-shaking'ом в продакшен-сборках.
Guards и Actions
ts
type Guard<TContext, TEvent> = (context: TContext, event: EventObject<TEvent>) => boolean
type Action<TContext, TEvent> = (
context: TContext,
event: EventObject<TEvent>,
) => void | Partial<TContext> | Promise<Partial<TContext> | void>Правила для guard:
- Должен быть синхронным и без побочных эффектов — также вызывается реактивно из
can() - Исключение внутри guard перехватывается и трактуется как
false - Возвращаемые значения
Promiseне ожидаются — используйте actions для асинхронной работы
Правила для action:
- Может быть
async— очередь событий ожидает каждый action перед выполнением следующего - Верните
Partial<TContext>, чтобы слить обновления в контекст; вернитеvoidтолько для побочных эффектов - Actions выполняются по порядку:
exit→transition.actions→entry
ts
// Action, обновляющий контекст
const incrementAttempts = (ctx: { attempts: number }) => ({
attempts: ctx.attempts + 1,
})
// Асинхронный action — результат fetch сливается в контекст
const loadUser = async (ctx, event: { type: 'LOAD'; id: number }) => {
const user = await api.getUser(event.id)
return { user }
}
// Guard
const canRetry = (ctx: { attempts: number }) => ctx.attempts < 3