Прежде всего, я надеюсь, что во время этой пандемии все будут в безопасности!
Учитывая текущую ситуацию, я участвовал в нескольких групповых чатах, в которых участвовали викторины, названия фильмов которых были описаны в Emojis. Я подумал, что это было довольно весело, но то, что я видел, зависело от размера группы и доступности людей, что часто люди отвечали ответами в групповом чате, что означало, что у других не было возможности играть целиком. викторина, не видя ответов, и это также означало, что вы часто теряли из виду вопросы.
Я подумал, что было бы забавно поиграться с созданием для этой цели простой онлайн-игры 😀.
Я развернул приложение Nuxt, создал проект Firebase, чтобы я мог использовать услуги хостинга и Firestore, а затем начал вырабатывать свою простую идею (которую можно увидеть в Интернете здесь).
Конечные автоматы (FSM)
Я много слышал о FSM от коллег и Twitterverse и подумал, что это будет хорошей возможностью для меня окунуться и опробовать одну из них.
Что такое конечные автоматы
Давайте начнем с самого начала, чтобы увидеть, что такое конечные автоматы и чем они могут быть полезны. Автомат - это способ описания конечного числа состояний, в которых приложение может находиться ровно в одном из в любой момент времени. Это означает, что я могу описать все состояния, в которых может находиться мое приложение, и могу быть уверен, что оно будет только в одном из этих состояний.
Как я создал автомат
Я начал с довольно грубого игрового компонента, который выглядел примерно так:
<template> <main> <Quiz v-if="!completed" :questions="questions" /> <Results v-if="completed && answered" :results="results" /> </main> </template>
Это может показаться не слишком сумасшедшим, поскольку это приложение довольно простое, но оно позволяет ему находиться в состоянии, когда, если игра завершена, но нет ответов, пользователь ничего не увидит. Конечно, легко решается, но тогда это неинтересно, и я хочу поиграть с автоматом.
Я решил создать свой первый автомат, используя xstate, и конечный автомат выглядит примерно так:
import { Machine, assign } from 'xstate' const gameMachine = Machine({ id: 'game', initial: 'play', context: { answered: 0, guesses: {} }, states: { play: { on: { RESOLVE: { target: 'results', actions: assign({ answered: (context, event) => event.answered, guesses: (context, event) => event.guesses }) } } }, results: { on: { RESET: 'play' }, initial: 'hide', states: { hide: { on: { TOGGLE: 'show' } }, show: { on: { TOGGLE: 'hide' } } } } } })
Что я нашел невероятно полезным при разработке, так это онлайн-визуализатор, который позволил мне переключаться между всеми состояниями и видеть его в действии.
Разбивая это на части, у меня есть два состояния play
или results
, внутри results
есть два дочерних состояния, которые позволяют пользователю показывать / скрывать ответы. Все очень просто. Действия в состоянии play
позволяют мне отправлять данные в gameMachine
, чтобы он мог содержать дополнительный контекст. Мой GameComponent
теперь выглядит так:
<main> <component :is="currentComponent" :questions="questions" :answers="answers" :answered="context.answered" :guesses="context.guesses" :show-answers="current.matches('results.show')" @answer="validate" @reset="send('RESET')" @toggleAnswers="send('TOGGLE')" /> </main>
Я использую динамический компонент Vue для рендеринга компонента в зависимости от того, в каком состоянии находится машина, компоненты $emit
родительскому компоненту, который затем отправляет сигнал gameMachine
, чтобы указать ему перейти в следующее состояние. Это означает, что моя игра никогда не может быть в состоянии, когда ничего не видно!
Использование FSM с Vue
Xstate предоставляет простой способ использования конечного автомата с Vue, а именно использование интерпретатора. Это обрабатывает такие вещи, как переходы между состояниями, выполнение событий и многое другое. Я бы рекомендовал прочитать документацию, чтобы получить полный список того, что он делает.
Чтобы использовать это в Vue / Nuxt, в вашем компоненте вам нужно будет импортировать или создать свой компьютер (рекомендуется держать свои машины вне компонента, чтобы их можно было совместно использовать / тестировать отдельно от компонента), затем запустить машину и прослушать к любым переходам между состояниями, чтобы вы могли обновить состояние. Это много, давайте посмотрим код:
<template> <div> My Current State {{ current.value }} <button @click="toggle()"> </div> </template> <script> import { Machine, interpret } from 'xstate' const demoMachine = Machine({ id: 'demo', initial: 'hello', context: { answered: 0, guesses: {} }, states: { hello: { on: { TOGGLE: 'goodbye' } }, goodbye: { on: { TOGGLE: 'hello' } } } }) export default { data: () => ({ demoMachine: interpret(demoMachine), current: demoMachine.initialState }), created () { // Start service on component creation this.demoMachine .onTransition((state) => { // Update the current state component data property with the next state this.current = state }) .start() }, methods: { toggle () { this.demoMachine.send('TOGGLE') } } } </script>
Разбив это на части, мы видим, что мы устанавливаем интерпретируемую машину на данные компонента вместе с начальным состоянием машины. Внутри хука created
жизненного цикла мы start
интерпретируем машину и слушаем переходы, в обратном вызове изменения перехода мы обновляем состояние компонентов из машины.
Спасибо за прочтение!
Первоначально опубликовано на https://mattchaffe.uk.