Прежде всего, я надеюсь, что во время этой пандемии все будут в безопасности!

Учитывая текущую ситуацию, я участвовал в нескольких групповых чатах, в которых участвовали викторины, названия фильмов которых были описаны в 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.