Создаем компонент Vue.js

Компонент — это обособленная группа кода, которая является самодостаточной, и направлена на решение какой-либо одной задачи. Компоненты позволяют расширить базовый набор HTML-элементов, путем добавления новых, инкапсулируя при это их внутреннюю структуру. То есть, мы можем взять группу базовых HTML-тегов, добавить к ним необходимые CSS стили и логику на JavaScript, и упаковать все это в новый HTML-компонент для повторного использования в любом месте приложения.

Предыдущая статья: Знакомство с SCSS при работе с Vue.js

Создание компонентов очень важная тема при работе с одностраничными JavaScript приложениями, потому что они очень быстро разрастаются и становятся очень сложными. А основной принцип, которым мы должны руководствоваться при разработке приложения, это контроль сложности. Большую и сложную систему необходимо разделять на небольшие самостоятельные части, которые можно легко изменять независимо друг от друга. «Разделяй и властвуй». И с этой задачей прекрасно справляются компоненты.

Для начала создадим в папке App новую папку Components, в которую и будем помечать все отдельные файлы компонентов. В созданной папке добавим файл test-scss.vue со следующим содержимым.

<template>
  <div>
    <h1>Hello World!</h1>
  </div>
</template>

<script>
  export default { }
</script>

В App.vue оставим только следующий код. Обратите внимание, что мы указали пока еще до конце не созданные компонент <test-scss>. Именно на это место будет подставлено содержимое, которое мы перенесли в отдельный файл.

<template>
  <test-scss></test-scss>
</template>

<script>
  export default { }
</script>

Теперь идем в файл index.js, чтобы зарегистрировать созданный нами компонент и сделать его доступным для использования. Импортируем его с помощью команды import TestScss from ‘./Components/test-scss.vue’. Здесь мы указываем, что содержимое из созданного нами файла будет доступно под именем TestScss. И теперь непосредственно регистрируем компонент командой Vue.component(‘test-scss’, TestScss). Соответственно первый параметр это имя, по которому мы будем обращаться к компоненту (<test-scss>), а второй это сам компонент.

import Vue from 'vue'
import VueRouter from 'vue-router'
import App from './App.vue'
import TestScss from './Components/test-scss.vue'

Vue.config.productionTip = false
Vue.use(VueRouter)
Vue.component('test-scss', TestScss)

const routes = [
    {
        path: '/',
        component: App
    }
]

const router = new VueRouter({
    routes,
    mode: 'history'
})

new Vue({
    el: '#app',
    template: "<div><router-view></router-view></div>",
    router
})

В итоге получаем ожидаемый результат.

Теперь перенесем в этот компонент все, что мы делали в предыдущем уроке, чтобы не загромождать App.vue, но при этом не потерять код. Обратите внимание, что необходимо изменить путь к файлам при импортировании (@import ‘../variables.scss’ и @import ‘../_h2partial’) , так как теперь путь ведется из другой папки.

test-scss.vue
<template>
  <div id="home">
    <nav>
      <ul>
        <li>text 1</li>
        <li>text 2</li>
      </ul>
    </nav>
    <h1>Hello World!</h1>
    <h2>Привет, мир!</h2>
    <ul>
      <li>text 3</li>
      <li>text 4</li>
    </ul>
    <div class="box">
      hello
    </div>
    <div class="success">OK</div>
    <div class="error">Error</div>
    <div class="marg">Отступ</div>
    <div class="wid">Светофор</div>
  </div>
</template>

<script>
  export default { }
</script>

<style lang="scss">
  @import '../variables.scss';

  nav {
  ul {
  margin: 0;
  padding: 0;
  color: red;
  list-style: none;
  }

  li { display: inline-block; }
  }

  h1 {
  color: $blue;
  }

  @import '../_h2partial';

  @mixin border-r($radius) {
  -webkit-border-radius: $radius;
  -moz-border-radius: $radius;
  -ms-border-radius: $radius;
  border-radius: $radius;
  }

  .box {
  @include border-r(10px);
  border-style: solid;
  max-width: 100px;
  }

  .message {
  border: 1px solid #ccc;
  padding: 10px;
  color: #333;
  }

  .success {
  @extend .message;
  border-color: green;
  }

  .error {
  @extend .message;
  border-color: red;
  }

  .marg {
  margin-left: 100px + 20px;
  padding-top: 10% + 5%;
  }

  $width: 150px;
  .wid {
  @if($width < 200px) {
      color: red;
    }
    @else if ($width > 700) {
      color: green;
    }
    @else {
      color: yellow;
    }
    
    width: $width;
  }
</style>

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

Но давайте рассмотрим более серьезный компонент. Для начала подключим CSS-фреймворк Bootstrap 4 версии. Он уже включен в набор наших пакетов, и нам достаточно добавить ссылку на в фале _Layout.cshtml.

<link rel="stylesheet" href="~/css/bootstrap.min.css" />

Теперь создадим еще один компонент question.vue в папке App/Components со следующим содержимым.

<template>
  <div id="question">
    <div class="card text-center">
      <div class="card-header">{{title}}</div>
      <div class="card-body">
        <h5 class="card-title">{{subtitle}}</h5>
        <p class="card-text">{{text}}</p>
      </div>
      <div class="card-footer text-muted">
        <input type="text" class="form-control" placeholder="Введите ответ" v-model="answer"></input>
        <a href="#" class="btn btn-primary" @click="showAnswer()">Ответить</a>
      </div>
    </div>
  </div>
</template>

<script>
  export default {
    data() {
      return {
        title: "Заголовок",
        subtitle: "Подзаголовок вопроса",
        text: "Текст вопроса",
        answer: ""
      }
    },
    methods: {
      showAnswer() {
        alert(this.answer);
      }
    }
  }
</script>

<style lang="scss">
  
</style>

Давайте рассмотрим по-порядку. Мы можем использовать переменные из блока данных, указывая имя переменной в двойных фигурных скобках. Ниже приведены примеры получения значений из переменных:

<div class="card-header">{{title}}</div>
<h5 class="card-title">{{subtitle}}</h5>
<p class="card-text">{{text}}</p>

А вот так мы объявляем переменные в области данных:

data() {
  return {
    title: "Заголовок",
    subtitle: "Подзаголовок вопроса",
    text: "Текст вопроса",
    answer: ""
  }
}

Мы также можем сохранять значение из поля ввода в переменных, для этого мы должны указать связь переменной и элемента формы с помощью команды v-model=»», в качестве аргумента указывается имя переменной. При изменении данных в поле ввода, они будут автоматически синхронизироваться с переменной, и наоборот.

<input type="text" class="form-control" placeholder="Введите ответ" v-model="answer"></input>

Ну и наконец мы можем объявлять методы и связывать их с событиями. Например, мы можем на добавить обработчик события на нажатие по ссылке с помощью команды @click=»». В качестве аргумента указывается имя метода и при необходимости параметры.

<a href="#" class="btn btn-primary" @click="showAnswer()">Ответить</a>

Сами же методы реализуются в блоке methods.

methods: {
  showAnswer() {
    alert(this.answer);
  }
}

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

Исходный код доступен в репозитоии github.