Создание викторины на Vue.js

в 15:46, , рубрики: javascript, tutorial, vue, vue.js, vuejs

Vue.js

Я не понимаю Angular. Мне очень нравится React, но я все еще изучаю его основы. Давайте попробуем Vue. Я расскажу, как я сделал микро-викторину.

Инициализация

HTML

Сначала создайте небольшой HTML-скелет и подключите Vue.

<html>
<body>
  <!-- Vue работает с этим div-ом -->
  <div id="app">
    <!-- Значение data.title из Vue отобразится здесь -->
    <h1>{{ title }}</h1>
  </div>

  <!-- Загружаем скрипт Vue -->
  <script src="https://vuejs.org/js/vue.js"></script>
  <!-- После загрузки страницы инициализируем Vue -->
  <script>
    window.onload=function(){
      new Vue({
        el: '#app', // Vue работает с этим div-ом (Смотри строку 4)
        data: { title: 'Hello.' }, // title установлен в значение 'Hello.'
      });
    }
  </script>
</body>

Сохраните его как foo.html и откройте в браузере. Вы должны увидить Hello., — это значит, что все работает. Мы только что научились отображать данные с помощью синтаксиса Mustache: {{ title }}

Данные

Затем создайте простой объект викторины и добавьте его к данным экземпляра Vue.

<script>
    window.onload=function(){
    // Создайте викторину с заголовком и двумя вопросами.
    // Вопрос имеет один или несколько ответов и один или несколько ответов могут быть верными.
      var quiz = {
        title: 'Моя викторина',
        questions: [
          {
            text: "Вопрос 1",
            responses: [
              {text: 'Неправильно, очень плохо.'}, 
              {text: 'Правильно!', correct: true}, 
            ]
          }, {
            text: "Вопрос 2",
            responses: [
              {text: 'Правильный ответ', correct: true}, 
              {text: 'Неправильный ответ'}, 
            ]
          } 
        ]
      };

      new Vue({
        el: '#app',
        data: { quiz: quiz }, // Добавьте объект викторины к данным Vue
      });
    }
</script>

Это наша викторина, следующий шаг — отобразить ее с помощью циклов Vue.

Отображение данных в HTML

Vue предоставляет декларативную систему циклов добавлением в элемент HTML аттрибута v-for="item on items".

<div id="app">
  <!-- Заголовок викторины -->
  <h1>{{ quiz.title }}</h1>
  <!-- Вопросы: показывать div для каждого вопроса -->
  <div v-for="question in quiz.questions">
    <h2>{{ question.text }}</h2>
    <!-- Ответы: Показывать li с радио-кнопкой для каждого возможного ответа -->
    <ol>
      <li v-for="response in question.responses">
        <label>
          <input type="radio"> {{response.text}}
        </label>
      </li>
    </ol>
  </div>
</div>

Vue имеет ненавязчивую реактивную систему. Если объект данных изменяется, HTML-отображение обновляется. Теперь викторина полностью готова, она живая и реактивная.

До сих пор все вопросы отображались на одной странице. Давайте изменим викторину так, чтобы отображать только один вопрос на странице. Чтобы сделать это, мы должны добавить кнопки «Следующий» и «Предыдущий» для каждого вопроса и скрыть все вопросы, которые не являются текущим.

Экземпляр Vue: методы навигации

Приложение, которое мы создали, должно иметь дело с пользовательским вводом: нажатиями на кнопки. Сначала добавьте поле текущего индекса вопроса и два метода навигации в экземпляр Vue.

new Vue({
  el: '#app',
  data: {
    quiz: quiz,
    // Хранит индекс текущего вопроса
    questionIndex: 0,
  },
  // Представление вызовет эти методы при клике
  methods: {
    // Перейти к следующему вопросу
    next: function() {
      this.questionIndex++;
    },
    // Вернуться к предыдущему вопросу
    prev: function() {
      this.questionIndex--;
    }
  }
});

Поле questionIndex увеличивается с помощью next() и уменьшается с помощью prev()

HTML: кнопки навигации

Создайте кнопку с директивой v-on:click="next", чтобы вызвать метод next(). Добавьте v-show="index === questionIndex" в контейнер вопросов, чтобы отображать только текущий вопрос (не забудьте добавить параметр index в директиву v-for)

<div id="app">
  <h1>{{ quiz.title }}</h1>
  <!-- Индекс используется для проверки текущего индекса вопроса -->
  <div v-for="(question, index) in quiz.questions">
    <!-- Скрыть все вопросы, показывать только один вопрос с текущим индексом -->
    <div v-show="index === questionIndex">
      <h2>{{ question.text }}</h2>
      <ol>
        <li v-for="response in question.responses">
          <label>
            <input type="radio"> {{response.text}}
          </label>
        </li>
      </ol>
      <!-- Кнопки навигации -->
      <!-- Заметка: Кнопка "Предыдущий" должна быть скрыта для первого вопроса -->
      <button v-if="questionIndex > 0" v-on:click="prev">
        Предыдущий
      </button>
      <button v-on:click="next">
        Следующий
      </button>
    </div>
  </div>
</div>

Теперь мы можем передвигаться внутри викторины. Последняя страница пустая, на ней мы должны отобразить результаты викторины.

Результат викторины

Чтобы отобразить результат викторины, мы должны обрабатывать пользовательские ответы на каждый вопрос. Это был самый трудный шаг для меня.

Обработка ответов пользователей

Прямо сейчас, радио-кнопки викторины не имеют привязки к Vue: клик по радио-кнопке не имеет эффекта. Директива v-model создает двустороннюю связь между вводом формы и состоянием приложения. Замените ранее созданную радио-кнопку.

<!-- Для каждого ответа текущего вопроса -->
<li v-for="response in question.responses">
  <label>
    <!-- Радио-кнопка имеет 3 новые директивы -->
    <!-- v-bind:value устанавливает "value" в значение "true" если ответ правильный -->
    <!-- v-bind:name устанавливает "name" в значение индекса вопроса для группировки ответов по вопросу -->
    <!-- v-model создает связь с userResponses -->
    <input type="radio" 
           v-bind:value="response.correct" 
           v-bind:name="index" 
           v-model="userResponses[index]"> {{response.text}}
  </label>
</li>

Атрибуты name и value привязаны к данным Vue. Затем инициализируйте userResponses в экземпляре Vue.

new Vue({
  el: '#app',
  data: {
    quiz: quiz,
    questionIndex: 0,
    // Массив инициализируется со значением "false" для всех вопросов
    // Это означает: "Пользователь верно ответил на вопрос n?" "Нет".
    userResponses: Array(quiz.questions.length).fill(false)
  },
  methods: {
    // ...
  }
});

Это был последний шаг для обработки пользовательских ответов: userResponses содержит живой массив «true, если ответ правильный». Вы можете перемещаться вперед и назад, выбирать и изменять ответы и видеть обновления userResponses.

Показ счета

Конечный счет — сумма «true» значений в userResponses. Создайте метод в приложении Vue, чтобы вычислять этот счет.

new Vue({
  // ...
  methods: {
    // ...
    // Возвращает количесво "true" в userResponses
    score: function() {
      return this.userResponses.filter(function(val) { return val }).length;
    }
  }
});

Затем добавьте итоговую страницу викторины с общим счетом.

<div id="app">
  <h1>{{ quiz.title }}</h1>
  <div v-for="(question, index) in quiz.questions">
    <!-- //... -->
  </div>
  <!-- Последняя страница, викторина завершена, показываем счет -->
  <div v-show="questionIndex === quiz.questions.length">
    <h2>
    Викторина завершена
  </h2>
    <p>
      Счет: {{ score() }} / {{ quiz.questions.length }}
    </p>
  </div>
</div>

Вот и все, викторина работает. Вы можете попробовать ее или загрузить код.

Послесловие

Мои навыки в javascript немного хромают и я только начал заниматься Vue. Я хотел поделиться чувством, как легко начать с ним работу. Не стесняйтесь критиковать меня.

Автор: Corpsee

Источник


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js