1. Введение

Что такое Vue.js? — Vue (произносится как /vjuː/, как и view) — это прогрессивный фреймворк для создания пользовательских интерфейсов. В отличие от других монолитных фреймворков, Vue с самого начала разработан так, чтобы его можно было внедрять постепенно. Базовая библиотека ориентирована только на уровень представления, и ее легко подобрать и интегрировать с другими библиотеками или существующими проектами. С другой стороны, Vue также отлично подходит для сложных одностраничных приложений при использовании в сочетании с современными инструментами и вспомогательными библиотеками.

Vue — это простая и минимальная прогрессивная среда JavaScript, которую можно использовать для постепенного создания мощных веб-приложений.

Vue — это легкая альтернатива другим средам JavaScript, таким как AngularJS.

Предпосылки

Для начала нам понадобится Vue CLI. Интерфейс командной строки предоставляет средства для быстрого формирования одностраничных приложений, и в кратчайшие сроки у вас будет приложение, работающее с горячей перезагрузкой, анализом при сохранении и готовыми к работе сборками.

Vue CLI предлагает инструмент разработки с нулевой конфигурацией для быстрого запуска ваших приложений и компонентов Vue.

$ npm установить --global vue-cli

Создание приложения Vue 2

$ vue init веб-пакет todo-приложение

$ cd todo-приложение

$ нпм установить

$ npm запустить разработчик

Это немедленно откроет ваш браузер и направит вас на http://localhost:8080. Страница будет выглядеть следующим образом.

Для оформления нашего приложения мы будем использовать Semantic. Semantic — это среда разработки, которая помогает создавать красивые, отзывчивые макеты с использованием удобного для человека HTML. Мы также будем использовать Sweetalert, чтобы предлагать пользователям подтвердить действия. Sweetalert — это библиотека, которая предоставляет красивые альтернативы стандартным предупреждениям JavaScript. Добавьте уменьшенные скрипты JavaScript и CSS и ссылки в файл index.html, который находится в корне вашей структуры папок.

<head>
  <meta charset="utf-8">
  <title>todo-app</title>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
  <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.2.7/semantic.min.css">
  <script src="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.2.7/semantic.min.js"></script>
  <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/sweetalert/1.1.3/sweetalert.min.css">
  <script src="https://cdnjs.cloudflare.com/ajax/libs/sweetalert/1.1.3/sweetalert.min.js"></script>
</head>

Компонентная структура

Каждое приложение Vue должно иметь компонент верхнего уровня, который служит основой для всего приложения. Для нашего приложения у нас будет основной компонент, внутри которого должен быть компонент TodoList. Внутри этого будут подкомпоненты todo.

Основной компонент приложения

Давайте погрузимся в создание нашего приложения. Во-первых, мы начнем с основного компонента верхнего уровня. Vue CLI уже генерирует основной компонент, который можно найти в src/App.vue. Мы создадим другие необходимые компоненты.

Создание компонента

Vue CLI создает компонент Hello во время настройки, который можно найти в src/components/Hello.vue. Мы создадим свой собственный компонент под названием TodoList.vue, и он нам больше не понадобится.

Внутри нового файла TodoList.vue напишите следующее.

<template>
  <div>
    <ul>
        <li> Todo A </li> 
        <li> Todo B </li> 
        <li> Todo C </li> 
    </ul> 
  </div>
</template>

<script type = "text/javascript" >

export default {
};
</script>
<style>
</style>

Файл компонента состоит из трех частей; шаблон, класс компонента и разделы стилей. Область шаблона — это визуальная часть компонента. Поведение, события и хранение данных для шаблона обрабатываются классом. Раздел стилей служит для дальнейшего улучшения внешнего вида шаблона.

Импорт компонентов

Чтобы использовать только что созданный компонент, нам нужно импортировать его в наш основной компонент. Внутри src/App.vue внесите следующие изменения чуть выше раздела скрипта и ниже закрывающего тега шаблона.

// add this line
import TodoList from './components/TodoList'  
// remove this line
import Hello from './components/Hello'

Нам также нужно будет сослаться на компонент TodoList в свойстве components и удалить предыдущую ссылку на компонент Hello. После изменений наш скрипт должен выглядеть так.

<script>
import TodoList from './components/TodoList';
export default {
  components: {
    // Add a reference to the TodoList component in the components property
    TodoList,
  },
};
</script>

Чтобы отобразить компонент, мы вызываем его как элемент HTML. Составные слова разделяются тире, как показано ниже, вместо верблюжьего регистра.

<template>
  <div>
    // Render the TodoList component
    // TodoList becomes
    <todo-list></todo-list>
  </div>
</template>

Когда мы сохранили наши изменения, наше элементарное приложение должно выглядеть так.

Добавление данных компонента

Нам нужно будет предоставить данные основному компоненту, который будет использоваться для отображения списка задач. Наши задачи будут иметь три свойства; Название, проект и выполнено (чтобы указать, завершена задача или нет). Компоненты передают данные в соответствующие шаблоны с помощью функции данных. Эта функция возвращает объект со свойствами, предназначенными для шаблона. Давайте добавим некоторые данные в наш компонент.

export default {
  name: 'app',
  components: {
    TodoList,
  },
  // data function avails data to the template
  data() {
    return {
      todos: [{
        title: 'Todo A',
        project: 'Project A',
        done: false,
      }, {
        title: 'Todo B',
        project: 'Project B',
        done: true,
      }, {
        title: 'Todo C',
        project: 'Project C',
        done: false,
      }, {
        title: 'Todo D',
        project: 'Project D',
        done: false,
      }],
    };
  },
};

Нам нужно будет передать данные из основного компонента в компонент TodoList. Для этого мы будем использовать директиву v-bind. Директива принимает аргумент, который обозначается двоеточием после имени директивы. Нашим аргументом будет todos, который указывает директиве v-bind привязать атрибут todos элемента к значению выражения todos.

<todo-list v-bind:todos="todos"></todo-list>

Теперь задачи будут доступны в компоненте TodoList как todos. Нам придется изменить наш компонент TodoList, чтобы получить доступ к этим данным. Компонент TodoList должен объявить свойства, которые он будет принимать при его использовании. Мы делаем это, добавляя свойство в класс компонента.

export default {  
    props: ['todos'],
}

Зацикливание и рендеринг данных

Внутри нашего шаблона TodoList можно просмотреть список задач, а также показать количество выполненных и незавершенных задач. Чтобы отобразить список элементов, мы используем директиву v-for. Синтаксис для этого представлен как v-for="item in items", где элементы — это массив с нашими данными, а элемент — представление элемента массива, по которому выполняется итерация.

<template>
  <div>
    // JavaScript expressions in Vue are enclosed in double curly brackets.
    <p>Completed Tasks: {{todos.filter(todo => {return todo.done === true}).length}}</p>
    <p>Pending Tasks: {{todos.filter(todo => {return todo.done === false}).length}}</p>
    <div class='ui centered card' v-for="todo in todos">
      <div class='content'>
        <div class='header'>
          {{ todo.title }}
        </div>
        <div class='meta'>
          {{ todo.project }}
        </div>
        <div class='extra content'>
          <span class='right floated edit icon'>
            <i class='edit icon'></i>
          </span>
        </div>
      </div>
      <div class='ui bottom attached green basic button' v-show="todo.done">
        Completed
      </div>
      <div class='ui bottom attached red basic button' v-show="!todo.done">
        Complete
      </div>
  </div>
</template>
<script type = "text/javascript" >
export default {
  props: ['todos'],
};
</script>

Редактирование задачи

Давайте извлечем шаблон todo в отдельный компонент для более чистого кода. Создайте новый файл компонента Todo.vue в src/components и перенесите шаблон todo. Теперь наш файл должен выглядеть так:

<template>
  <div class='ui centered card'>
    <div class='content'>
        <div class='header'>
            {{ todo.title }}
        </div>
        <div class='meta'>
            {{ todo.project }}
        </div>
        <div class='extra content'>
            <span class='right floated edit icon'>
            <i class='edit icon'></i>
          </span>
        </div>
    </div>
    <div class='ui bottom attached green basic button' v-show="todo.done">
        Completed
    </div>
    <div class='ui bottom attached red basic button' v-show="!todo.done">
        Complete
    </div>
</div>
</template>
<script type="text/javascript">
  export default {
    props: ['todo'],
  };
</script>

В компоненте TodoList рефакторинг кода для отображения компонента Todo. Нам также нужно будет изменить способ передачи наших задач в компонент Todo. Мы можем использовать атрибут v-for для любых компонентов, которые мы создаем, точно так же, как и для любого другого элемента. Синтаксис будет таким: <my-component v-for="item in items" :key="item.id"></my-component>. Обратите внимание, что начиная с версии 2.2.0 и выше при использовании v-for с компонентами требуется ключ. Важно отметить, что при этом данные не передаются компоненту автоматически, поскольку компоненты имеют свои собственные изолированные области. Чтобы передать данные, мы должны использовать реквизит.

<my-component v-for="(item, index) in items" v-bind:item="item" v-bind:index="index">
</my-component>

Наш рефакторинг шаблона компонента TodoLost:

<template>
  <div>
    <p>Completed Tasks: {{todos.filter(todo => {return todo.done === true}).length}}</p>
    <p>Pending Tasks: {{todos.filter(todo => {return todo.done === false}).length}}</p>
   // we are now passing the data to the todo component to render the todo list
    <todo  v-for="todo in todos" v-bind:todo="todo"></todo>
  </div>
</template>
<script type = "text/javascript" >
import Todo from './Todo';
export default {
  props: ['todos'],
  components: {
    Todo,
  },
};
</script>

Давайте добавим в класс компонента Todo свойство isEditing. Это будет использоваться для определения того, находится ли Todo в режиме редактирования или нет. У нас будет обработчик событий в диапазоне редактирования в шаблоне. Это вызовет метод showForm, когда он будет нажат. Это установит для свойства isEditing значение true. Прежде чем мы взглянем на это, мы добавим форму и установим условные выражения, чтобы показать задачу или форму редактирования в зависимости от того, имеет ли свойство isEditing значение true или false. Теперь наш шаблон должен выглядеть так.

<template>
  <div class='ui centered card'>
    // Todo shown when we are not in editing mode.
    <div class="content" v-show="!isEditing">
      <div class='header'>
          {{ todo.title }}
      </div>
      <div class='meta'>
          {{ todo.project }}
      </div>
      <div class='extra content'>
          <span class='right floated edit icon' v-on:click="showForm">
          <i class='edit icon'></i>
        </span>
      </div>
    </div>
    // form is visible when we are in editing mode
    <div class="content" v-show="isEditing">
      <div class='ui form'>
        <div class='field'>
          <label>Title</label>
          <input type='text' v-model="todo.title" >
        </div>
        <div class='field'>
          <label>Project</label>
          <input type='text' v-model="todo.project" >
        </div>
        <div class='ui two button attached buttons'>
          <button class='ui basic blue button' v-on:click="hideForm">
            Close X
          </button>
        </div>
      </div>
    </div>
    <div class='ui bottom attached green basic button' v-show="!isEditing &&todo.done" disabled>
        Completed
    </div>
    <div class='ui bottom attached red basic button' v-show="!isEditing && !todo.done">
        Pending
    </div>
  </div>
</template>
</template>

В дополнение к методу showForm нам потребуется добавить метод hideForm для закрытия формы при нажатии кнопки отмены. Давайте посмотрим, как теперь выглядит наш скрипт.

<script>
export default {
  props: ['todo'],
  data() {
    return {
      isEditing: false,
    };
  },
  methods: {
    showForm() {
      this.isEditing = true;
    },
    hideForm() {
      this.isEditing = false;
    },
  },
};
</script>

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

Удаление задачи

Давайте начнем с добавления значка для удаления Todo прямо под значком редактирования.

<template>
    <span class='right floated edit icon' v-on:click="showForm">
      <i class='edit icon'></i>
    </span>
    /* add the trash icon in below the edit icon in the template */
    <span class='right floated trash icon' v-on:click="deleteTodo(todo)">
      <i class='trash icon'></i>
    </span>
</template>

Далее мы добавим в класс компонента метод для обработки щелчка по значку. Этот метод отправит событие delete-todo родительскому компоненту TodoList и передаст текущую задачу для удаления. Мы добавим прослушиватель событий для удаления значка.

<span class='right floated trash icon' v-on:click="deleteTodo(todo)">
// Todo component
methods: {
    deleteTodo(todo) {
      this.$emit('delete-todo', todo);
    },
  },

Родительскому компоненту (TodoList) потребуется обработчик событий для обработки удаления. Давайте определим это.

// TodoList component
methods: {
    deleteTodo(todo) {
      const todoIndex = this.todos.indexOf(todo);
      this.todos.splice(todoIndex, 1);
    },
  },

Метод deleteTodo будет передан компоненту Todo следующим образом.

// TodoList template
<todo  v-on:delete-todo="deleteTodo" v-for="todo in todos" v-bind:todo="todo"></todo>

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

Добавление новой задачи

Чтобы создать новую задачу, мы начнем с создания нового компонента CreateTodo в src/components. Это отобразит кнопку со знаком плюс, которая при нажатии превратится в форму. Это должно выглядеть примерно так.

<template>
  <div class='ui basic content center aligned segment'>
    <button class='ui basic button icon' v-on:click="openForm" v-show="!isCreating">
      <i class='plus icon'></i>
    </button>
    <div class='ui centered card' v-show="isCreating">
      <div class='content'>
        <div class='ui form'>
          <div class='field'>
            <label>Title</label>
            <input v-model="titleText" type='text' ref='title' defaultValue="">
          </div>
          <div class='field'>
            <label>Project</label>
            <input type='text' ref='project' defaultValue="">
          </div>
          <div class='ui two button attached buttons'>
            <button class='ui basic blue button' v-on:click="sendForm()">
              Create
            </button>
            <button class='ui basic red button' v-on:click="closeForm">
              Cancel
            </button>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>
<script>
export default {
  data() {
    return {
      titleText: '',
      projectText: '',
      isCreating: false,
    };
  },
  methods: {
    openForm() {
      this.isCreating = true;
    },
    closeForm() {
      this.isCreating = false;
    },
    sendForm() {
      if (this.titleText.length > 0 && this.projectText.length > 0) {
        const title = this.titleText;
        const project = this.projectText;
        this.$emit('create-todo', {
          title,
          project,
          done: false,
        });
        this.newTodoText = '';
      }
      this.isCreating = false;
    },
  },
};
</script>

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

// Main Component App.vue
components: {
    TodoList,
    CreateTodo,
  },

Мы также добавим метод для создания новых задач.

// App.vue
methods: {
    addTodo(title) {
      this.todos.push({
        title,
        done: false,
      });
    },
  },

Компонент CreateTodo будет вызываться в шаблоне App.vue следующим образом:

<create-todo v-on:add-todo="addTodo">

Завершение задачи

Наконец, мы добавим метод completeTodo в компонент Todo, который отправляет событие complete-todo родительскому компоненту при нажатии кнопки ожидания и устанавливает статус выполнения задачи в true.

// Todo component
methods: {
      completeTodo(todo) {
        this.$emit('complete-todo', todo);
      },
}

Обработчик события будет добавлен к компоненту TodoList, обрабатывающему событие.

methods: {
    completeTodo(todo) {
      const todoIndex = this.todos.indexOf(todo);
      this.todos[todoIndex].done = true;
    },
  },

Чтобы передать метод TodoList компоненту Todo, мы добавим его в вызов компонента Todo.

<todo v-on:delete-todo="deleteTodo" v-on:complete-todo="completeTodo" v-for="todo in todos" :todo.sync="todo"></todo>

Вывод

Мы узнали, как инициализировать приложение Vue с помощью интерфейса командной строки Vue. Кроме того, мы узнали о структуре компонентов, добавлении данных в компоненты, прослушивателях событий и обработчиках событий. Мы увидели, как создать задачу, отредактировать ее и удалить. Нам еще многое предстоит узнать. Мы использовали статические данные в нашем основном компоненте. Следующим шагом будет получение данных с сервера и их соответствующее обновление. Теперь мы готовы создать интерактивное приложение Vue. Попробуйте что-нибудь еще сами и посмотрите, как пойдет. Ваше здоровье!