Skip to content

Общий доступ и инструменты ​

Общий доступ к машине между компонентами ​

useSharedMachine(config, options?) создаёт или получает singleton-инстанс машины по config.id. Полезно, когда несвязанным компонентам нужно делить одну и ту же запущенную машину без прокидывания пропсов или Pinia.

ts
function useSharedMachine<TState, TEvent, TContext>(
  config: MachineConfig<TState, TEvent, TContext>,
  options?: UseMachineOptions,
): MachineInstance<TState, TEvent, TContext>

Требует установленный VueMachinePlugin.

ts
// В компоненте A
const { state } = useSharedMachine(cartMachine)

// В компоненте B (полностью отдельное дерево)
const { send } = useSharedMachine(cartMachine)

// Оба используют один и тот же инстанс машины — одно состояние, один контекст
await send('ADD_ITEM') // state.value компонента A обновляется реактивно

Если машина с config.id уже зарегистрирована в хранилище, возвращается существующий инстанс (при этом внутренний счётчик ссылок хранилища для этого id увеличивается — см. Прямой доступ к хранилищу). Иначе создаётся новый и регистрируется автоматически.

Заметка: свои options (включая persist) применяет только первый вызов useSharedMachine(config, options) для данного id — каждый последующий вызов в любом другом месте дерева получает то, что настроил первый вызывающий, независимо от того, что передаёт он сам. В разработке, если options последующего вызова заметно отличаются от тех, с которыми был реально создан общий инстанс (historyLimit, либо persist.key/persist.storage), в консоль выводится console.warn, чтобы расхождение не осталось незамеченным — но собственные options последующего вызова всё равно молча игнорируются.

Vue-плагин ​

Установите VueMachinePlugin, чтобы включить глобальный реестр машин (useMachineStore, useSharedMachine) и интеграцию с DevTools.

ts
import { createApp } from 'vue'
import { VueMachinePlugin } from '@macrulez/vue-state-machine'
import App from './App.vue'

const app = createApp(App)
app.use(VueMachinePlugin)
app.mount('#app')

Прямой доступ к хранилищу ​

useMachineStore() предоставляет прямой доступ к глобальному реестру. Полезно для отладки или админ-панелей.

ts
const store = useMachineStore()

store.register('cart', instance) // зарегистрировать вручную (опционально: register('cart', instance, options))
store.retain('cart') // отметить ещё одного пользователя уже зарегистрированного id, не заменяя его
store.unregister('cart') // освободить одно использование; запись реально удаляется только когда каждому register()/retain() найдётся парный unregister()
store.get('cart') // MachineInstance | undefined
store.getOptions('cart') // UseMachineOptions, с которыми был реально создан текущий инстанс, либо undefined
store.getAll() // Map<string, MachineInstance>

Вызов useMachineStore() без установленного плагина выбрасывает содержательную ошибку.

Хранилище ведёт подсчёт ссылок: register() и retain() каждый увеличивают внутренний счётчик для id, а unregister() уменьшает его, реально удаляя запись только когда счётчик достигает нуля. useMachine() (а следовательно и useWizard(), построенный поверх него) автоматически вызывает store.unregister(id) в onUnmounted, а useSharedMachine() делает то же самое для каждого вызывающего, который переиспользует существующую запись через retain() — поэтому машина, общая для нескольких компонентов через один id, живёт, пока не размонтируется каждый из использующих её компонентов, а не только тот, что создал её первым. Если вы вызываете store.register() самостоятельно, в обход этих composable-функций, вы сами отвечаете за парный вызов store.unregister().

Заметка: в разработке store.register(id, ...) выводит console.warn, если id уже зарегистрирован — обычно это значит, что два вызова defineMachine()/useMachine() (либо явный WizardOptions.id) непреднамеренно используют один и тот же id. Прежний инстанс всё равно перезаписывается в любом случае; предупреждение — это только видимость проблемы, не защита от неё.

DevTools ​

Интеграция с DevTools живёт в отдельной точке входа, поэтому никогда не попадает в продакшен-бандлы.

ts
import { createApp } from 'vue'
import { VueMachinePlugin } from '@macrulez/vue-state-machine'
import { VueMachineDevtools } from '@macrulez/vue-state-machine/devtools'
import App from './App.vue'

const app = createApp(App)
app.use(VueMachinePlugin)

// Только в разработке
if (import.meta.env.DEV) {
  app.use(VueMachineDevtools)
}

app.mount('#app')

Возможности панели:

  • Регистрирует панель настроек «State Machines» в Vue DevTools
  • При каждом обновлении инспектора DevTools (visitComponentTree) отправляет одно событие таймлайна на каждую зарегистрированную машину (из MachineStore) с её текущим состоянием и контекстом — это живой снапшот, снимаемый при каждой инспекции дерева DevTools, а не push на каждый отдельный send(); переход, произошедший между двумя инспекциями, сам по себе не фиксируется

VueMachinePlugin должен быть установлен раньше VueMachineDevtools — он ищет хранилище через app._context.provides и предупреждает (не выбрасывает исключение), если плагин ещё не установлен.