Створюємо свій перший Vue.js компонент

19 хв. читання

Неважливо, наскільки добре задокументований фреймворк. Цього все одно недостатньо. Ознайомлення з теорією не завжди допомагає самостійно реалізувати отримані знання.

У цій статті ми створимо систему оцінювання за кількістю зірок й будемо посилатись на концепції Vue.js тільки за потреби вже під час написання коду, пояснюючи, навіщо вони нам потрібні.

Створюємо свій перший Vue.js компонент

TL;DR: у цьому матеріалі дуже детально описується весь процес. Він допоможе вам зрозуміти деякі основні концепції Vue.js та навчить, як приймати рішення для майбутніх проектів. Якщо не хочете витрачати свій час, можете подивитися на фінальний код через CodeSandbox.

Починаємо

Vue.js (по праву) гордий за себе через можливість запуску за допомогою звичайного додавання js скрипта, але ситуація змінюється з однофайловими компонентами. Тепер вам не потрібно створювати компоненти у такий спосіб. Ви можете обійтись глобальним компонентом з Vue.component. Проблема у тому, що з'являються побочні ефекти: необхідність використання шаблонів рядків, відсутність підтримки локального CSS, а також етапу збирання (так, без препроцесорів). Проте, ми хочемо поглибитись і навчитись створювати справжній компонент, який можна використовувати в реальному проекті. З цих причин ми будемо працювати з реальними налаштуваннями на базі Webpack.

Щоб скоротити час налаштування, ми використаємо шаблон vue-cli та Vue.js шаблон webpack-simple.

Для початку, вам потрібно встановити vue-cli глобально. Запустіть свій термінал і введіть:

npm install -g vue-cli

Тепер ви можете створювати готові до використання Vue.js шаблони у кілька кліків. Далі введіть:

vue init webpack-simple path/to/my-project

Вам буде запропоновано декілька варіантів. Виберіть для всіх параметри за умовчанням, за винятком «Use sass», на який слід відповідати yes (у). Тоді vue-cli ініціалізує проект і створить файл package.json. Коли це буде зроблено, ви зможете перейти до каталогу, встановити залежності та запустити проект:

cd path/to/my-project
npm install
npm run dev

Це воно! Webpack почне показувати ваш проект на порту 8080 (якщо є) і запустить його у вашому браузері. Якщо все пройшло добре, ви повинні побачити привітання, подібне до цього.

Створюємо свій перший Vue.js компонент

Ми вже дійшли до найцікавішого?

Майже! Щоб належним чином налаштувати компонент Vue.js, потрібні правильні інструменти. Перейдіть і встановіть розширення браузера Vue.js Devtools (Firefox /Chrome/Safari).

Ваш перший компонент

Однією з найкращих функцій Vue.js є однофайлові компоненти (SFC). Вони дозволяють визначати структуру, стиль та поведінку компонента в одному файлі без звичних недоліків змішування HTML, CSS та JavaScript.

SFC мають розширення .vue і таку структуру:

<template>
  <!-- Ваш HTML -->
</template>

<script>
  /* Ваш JS */
</script>

<style>
  /* Ваш CSS */
</style>

Напишемо наш перший компонент: створіть Rating.vue у /src/components та скопіюйте/вставте фрагмент коду вище. Відкрийте /src/main.js і адаптуйте наявний код:

import Vue from 'vue'
import Rating from './components/Rating'

new Vue({
  el: '#app',
  template: '<Rating/>',
  components: { Rating }
})

Додайте трохи HTML у свій Rating.vue:

<template>
  <ul>
    <li>One</li>
    <li>Two</li>
    <li>Three</li>
  </ul>
</template>

Тепер подивіться на сторінку у вашому браузері, там ви повинні побачити список! Vue.js додав ваш компонент <Rating> до елементу #app в index.html. Якщо ви перевіряєте HTML, ви не повинні бачити жодного знаку елемента #app: Vue.js замінив його компонентом.

Зауваження: чи помітили ви, що навіть не потрібно було перезавантажувати сторінку? Це тому, що Webpack vue-loader постачається з функцією гарячого перезавантаження (hot reload). На відміну від живого перезавантаження чи синхронізації браузера, гаряче перезавантаження не оновлює сторінку щоразу, коли ви змінюєте файл. Замість цього, він стежить за змінами компонентів і лише оновлює їх, не змінюючи стан.

Ми витратили деякий час на налаштування, тепер ми напишемо змістовний код.

Шаблон

Ми збираємося використовувати vue-awesome, компонент SVG іконок для Vue.js, побудований за допомогою Font Awesome. Це дозволяє нам завантажувати лише потрібні іконки. Встановіть його за допомогою npm (або Yarn):

npm install vue-awesome

Відредагуйте наступним чином:

<template>
  <div>
    <ul>
      <li><icon name="star"/></li>
      <li><icon name="star"/></li>
      <li><icon name="star"/></li>
      <li><icon name="star-o"/></li>
      <li><icon name="star-o"/></li>
    </ul>
    <span>3 of 5</span>
  </div>
</template>

<script>
  import 'vue-awesome/icons/star'
  import 'vue-awesome/icons/star-o'

  import Icon from 'vue-awesome/components/Icon'

  export default {
    components: { Icon }
  }
</script>

Тепер сповільнимося і спробуємо все обміркувати 😅

Vue.js використовує нативні модулі ES6 для обробки залежностей та експорту компонентів. Перші два рядки блоку script імпортують іконки самостійно, тому ви не отримаєте у вашому остаточному пакеті іконки, які вам не потрібні. Третій імпортує Icon компонент з vue-awesome, щоб ви могли використовувати його у вашому середовищі.

Icon це Vue.js SFC, як і той, який ми будуємо. Якщо ви відкриєте файл, ви побачите, що він має точно таку ж структуру.

Export default блок експортує об'єкт як модель представлення нашого компонента. Ми зареєстрували компонент Icon у властивостях components, щоб потім мати можливість його використовувати.

Нарешті, ми залучили компонент Icon у нашому HTML template і передали йому властивість name, щоб визначити, яку іконку ми хочемо реалізовувати. Компоненти можуть використовуватись як спеціальні теги HTML, перетворюючи їх у шаблонні набори (наприклад: MyComponent стає my-component). Нам не потрібно вставляти щось усередині компонента, тому ми використовували тег, що закривається самостійно.

Зауваження: ви помітили, що ми додали div навколо HTML? Це тому, що ми також приєднали лічильник до span на кореневому рівні, а шаблони компонентів у Vue.js приймають лише один кореневий елемент. Якщо ви не будете слідкувати за цим, то отримаєте помилку під час компіляції.

Стиль

Якщо ви колись використовували CSS, ви знаєте, що його відмінною рисою вважається глобальний характер. Вкладеність дуже довго вважалась вирішенням цієї проблеми. Тепер ми знаємо, що це не так.

Такі методології, як BEM, були винайдені для того, щоб обійти цю проблему, і зберігати низьку специфічність за іменами класів. Якийсь час це був ідеальний спосіб написати чистий і масштабований CSS. Потім з'явилися структури та бібліотеки, такі як Vue.js або React, і додали локальні стилі в таблицю.

React має стилізовані компоненти (styled components), Vue.js має компонентно-локальний CSS. Це дозволяє писати компоненто-специфічний CSS без необхідності придумування велосипеду для збереження його вмісту. Ви пишете звичайний CSS з іменами «звичайних» класів, а Vue.js обробляє область визначення, присвоюючи атрибути даних елементам HTML і додаючи його до скомпонованих стилів.

Додамо кілька простих класів до компонента:

<template>
  <div class="rating">
    <ul class="list">
      <li class="star active"><icon name="star"/></li>
      <li class="star active"><icon name="star"/></li>
      <li class="star active"><icon name="star"/></li>
      <li class="star"><icon name="star-o"/></li>
      <li class="star"><icon name="star-o"/></li>
    </ul>
    <span>3 of 5</span>
  </div>
</template>

І стилізуємо їх:

<style scoped>
  .rating {
    font-family: 'Avenir', Helvetica, Arial, sans-serif;
    font-size: 14px;
    color: #a7a8a8;
  }
  .list {
    margin: 0 0 5px 0;
    padding: 0;
    list-style-type: none;
  }
  .list:hover .star {
    color: #f3d23e;
  }
  .star {
    display: inline-block;
    cursor: pointer;
  }
  .star:hover ~ .star:not(.active) {
    color: inherit;
  }
  .active {
    color: #f3d23e;
  }
</style>

Бачите тут локальний атрибут? Ось що робить Vue.js задля масштабованості стилів, тому вони не будуть витікати ніде. Якщо ви копіюєте/вставляєте HTML-код прямо в index.html, ви помітите, що ваші стилі не застосовуються: це пов'язано з тим, що вони орієнтовані на компонент! 🎉

Як щодо препроцесорів?

Vue.js робить простим перехід від звичайного CSS до вашого улюбленого препроцесора. Все, що вам потрібно – правильний Webpack завантажувач і простий атрибут в блоці style. Під час створення проекту ми зазначили «yes» для «Use sass», тому для нас вже встановлено та налаштовано sass-cli. Тепер все, що нам потрібно зробити, це додати lang = "scss" до відкривального тегу style.

Далі ми можемо використовувати Sass, щоб писати стилі компонентів, імпортувати змінні, визначати кольори або міксини. Якщо ви віддаєте перевагу синтаксису (або «sass»), просто змініть scss в sass в атрибуті lang.

Поведінка

Тепер, коли наш компонент виглядає добре, настав час змусити його працювати. Зараз у нас є захардкодований шаблон. Створимо якийсь початковий макет і налаштуємо шаблон таким чином, щоб він відображався:

<script>
  ...
  export default {
    components: { Icon },
    data() {
      return {
        stars: 3,
        maxStars: 5
      }
    }
  }
</script>
<template>
  <div class="rating">
    <ul class="list">
      <li v-for="star in maxStars" :class="{ 'active': star <= stars }" class="star">
        <icon :name="star <= stars ? 'star' : 'star-o'"/>
      </li>
    </ul>
    <span>3 of 5</span>
  </div>
</template>

Тут ми використали Vue data для встановлення стану компонентів. Кожна властивість, яку ви визначаєте в data, стає реактивною: якщо вона змінюється, то це буде зображено у представленні.

Ми робимо багаторазовий компонент, тому data повинна бути фабричною функцією, а не літералом об'єкта. Таким чином, ми отримуємо новий об'єкт, а не посилання на чинний, який буде розділений кількома компонентами.

Наша фабрика data повертає дві властивості: stars, поточна кількість «активних» зірок і maxStars – загальна кількість зірок для компонента. З цих причин ми адаптували наш шаблон так, щоб він показував стан фактичного компонента. Vue.js поставляється з набором директив, які дозволяють додавати логіку презентації у наш шаблон, не змішуючи його з простим JavaScript. v-for проходять циклом над будь-яким ітерабельним об'єктом (масиви, об'єкти тощо). Він також може приймати номер як діапазон, який буде повторюватися x разів. Ось, що ми зробили з v-for = "star in maxStars", тому маємо li для кожної зірки в компоненті.

Можливо, ви помітили, що деякі властивості мають префікс двокрапкою: це скорочення для директиви v-bind, яка динамічно зв'язує атрибути до виразу. Ми могли б написати це у довгій формі, v-bind: class.

Коли зірка активна, ми повинні додати active клас до елементів li. У нашому випадку це означає, що кожен li, чий індекс менше, ніж stars, повинен мати active клас. Ми використали вираз в директиві :class додавати active лише тоді, коли поточна star менше, ніж stars. Ми використовували ту ж саму умову, на цей раз з трійковим оператором, щоб визначити, який Icon компонент використовувати: star або star-o.

А як щодо лічильника?

Тепер, коли наш список зірок пов'язаний з фактичними даними, настав час зробити те ж саме для лічильника. Найпростіший спосіб зробити це – використовувати текстову інтерполяцію з наступним синтаксисом:

<span>{{ stars }} of {{ maxStars }}</span>

Досить прямолінійно, чи не так? Якщо нам потрібен більш складний вираз JavaScript, то краще абстрагувати його в обчислених властивостях.

export default {
  ...
  computed: {
    counter() {
      return `${this.stars} of ${this.maxStars}`
    }
  }
}
<span>{{ counter }}</span>

Ось це надмірність. Ми можемо піти з виразом в шаблоні – і він все одно залишатиметься читабельним. Тим не менше, пам'ятайте про обчислювальні властивості, коли доводиться мати справу з більш складною логікою.

Інша річ, яку ми повинні зробити, це забезпечити спосіб для приховання лічильника. Найпростіший шлях – використовувати директиву v-if з boolean.

<span v-if="hasCounter">{{ stars }} of {{ maxStars }}</span>
export default {
  ...
  data() {
    return {
      stars: 3,
      maxStars: 5,
      hasCounter: true
    }
  }
}

Інтерактивність

Ми майже закінчили, але нам все ж доведеться реалізувати найбільш цікаву частину нашого компонента: реактивність. Ми використаємо v-on, директиву Vue.js, яка обробляє події та methods, властивість Vue.js, на яку ви можете приєднати всі свої методи.

<template>
  ...
  <li @click="rate(star)" ...>
  ...
</template>
export default {
  ...
  methods: {
    rate(star) {
      // щось робиться
    }
  }
}

Ми додали атрибут @click до li, який є скороченням для v-on:click. Ця директива містить виклик rate методу, який ми визначили в methods властивостях компонента.

"Почекайте хвилину ... це виглядає надзвичайно знайомим з атрибутом onclick HTML. Чи не повинно вважатись застарілою і поганою практикою використання inline JavaScript у HTML? "

Це правда, але попри те, що синтаксис виглядає так само, як onclick, порівнювати їх буде помилкою. Створюючи компонент Vue.js, ви не повинні думати про це як окремий HTML/CSS/JS, а як один компонент, який використовує декілька мов. Коли проект подається у браузері або компілюється для продакшену, всі HTML-документи та директиви складаються в простий JavaScript. Якщо ви перевіряєте зрендерений HTML, ви не побачите жодних ознак своїх директив, а також атрибутів onclick. Vue.js склав ваш компонент і створив відповідні біндинги. Це також означає, що ви маєте доступ до контексту компонента прямо з вашого шаблону: оскільки директиви пов'язані з моделлю перегляду. Всупереч традиційному проекту з окремим HTML, шаблон є невід'ємною частиною компонента.

Повернімося до нашого rate методу. Нам потрібно змінювати stars до індексу натиснутого елемента, тому ми передаємо індекс з директиви @click наступним чином:

export default {
  ...
  methods: {
    rate(star) {
      this.stars = star
    }
  }
}

Перевірте сторінку в своєму браузері та спробуйте натиснути зірочки: працює!

Якщо ви відкриєте панель Vue у вашій браузерній консолі розробника і виберете компонент Rating, ви побачите зміну даних, коли ви натискаєте на зірочки. Це означає, що властивість ваших stars є реактивною: коли ви її змінюєте, вона відправляє свої зміни на перегляд. Дана концепція називається зв'язуванням даних (data-binding), і вам повинно бути це знайомо, якщо коли-небудь використовували такі фреймворки, як Backbone.js або Knockout. Різниця полягає в тому, що Vue.js, як і React, робить це лише в одному напрямку: що називається одностороннім зв'язуванням даних. Але ця тема заслуговує на власну статтю 😊

На цьому етапі, ми могли б назвати наш проект закінченим, хоча ще можна покращити та допрацювати UX.

Зараз ми не можемо встановити нульову оцінку, тому що натискання по зірці встановлює значення до свого індексу. Краще повторно натиснути на одну і ту ж зірку і перемикнути її поточний стан, а не залишати активною.

export default {
  ...
  methods: {
    rate(star) {
      this.stars = this.stars === star ? star - 1 : star
    }
  }
}

Тепер, якщо індекс кліка зірки дорівнює поточному значенню stars, ми зменшуємо його значення. В іншому випадку призначимо йому значення star.

Для більш ретельного підходу, ми також повинні додати рівень контролю, щоб переконатися, що для stars ніколи не присвоюється безглузде значення. Нам потрібно переконатися, що stars ніколи не стає менше за 0, ніколи не більше, ніж maxStars, і є правильним числом.

export default {
  ...
  methods: {
    rate(star) {
      if (typeof star === 'number' && star <= this.maxStars && star >= 0) {
        this.stars = this.stars === star ? star - 1 : star
      }
    }
  }
}

Передавання властивостей

На даний час інформація про компонент захардкодована в data властивостях. Якщо ми хочемо, щоб наш компонент використовувався, ми повинні мати можливість передавати йому спеціальні дані з його екземплярів. У Vue.js ми робимо це за допомогою props.

export default {
  props: ['grade', 'maxStars', 'hasCounter'],
  data() {
    return {
      stars: this.grade
    }
  },
  ...
}

І в main.js:

new Vue({
  el: '#app',
  template: '<Rating :grade="3" :maxStars="5" :hasCounter="true"/>',
  components: { Rating }
})

Можна виділити три речі:

По-перше, ми використали v-bind для передачі властивостей з екземпляра компонента: ось що Vue.js називає динамічним синтаксисом. Вам це не потрібно, коли ви хочете передавати значення рядка, для якого буде працювати буквальний синтаксис (звичайний атрибут без v-bind). Але в нашому випадку, оскільки ми передаємо числа та логічні вирази, це дуже важливо.

Props та data об'єднуються під час компіляції, тому нам не потрібно змінювати спосіб, яким ми називаємо властивості, як у моделі представлення, так і у шаблоні. Але з цієї ж причини ми не можемо використовувати однакові імена для props і data.

Нарешті, ми визначили grade prop і передали як значення для stars у data властивість. Чому ми це зробили, замість того, щоб безпосередньо використовувати grade prop? Все через те, що значення буде змінено, коли ми змінюємо оцінку. У Vue.js, властивості передаються від предків до нащадків, а не навпаки, тому ви не випадково мутуєте стан предка. Це дозволить протистояти принципу потоку даних в одну сторону та ускладнити налагодження. Ось чому ви не повинні намагатися змінити підтримку всередині дочірнього компонента. Замість цього визначте локальну властивість data, яка використовує початкове значення властивостей як власне.

Завершення

Перед тим, як ми покінчимо з цим, є одна річ у Vue.js, про яку ми повинні пам'ятати: перевірка властивостей.

Vue.js дозволяє контролювати властивості, перш ніж вони передаються у компонент. Ви можете виконати чотири основні речі: перевірити тип, вимагати визначення властивостей, встановити значення за замовчуванням та виконати користувальницьку перевірку.

export default {
  props: {
    grade: {
      type: Number,
      required: true
    },
    maxStars: {
      type: Number,
      default: 5
    },
    hasCounter: {
      type: Boolean,
      default: true
    }
  },
  ...
}

Ми використовували перевірку типу, щоб переконатися, що потрібний тип даних передається компоненту. Це буде особливо корисним, якщо ми забудемо використовувати динамічний синтаксис для передачі нестандартних значень. Ми також переконалися, що значення grade було передано. Для інших властивостей ми визначили значення за замовчуванням, тому компонент працює, навіть якщо не передано жодних спеціальних даних.

Тепер ми можемо проаналізувати компонент виконавши наступне:

<Rating :grade="3"/>

І це все! Ви тільки що створили свій перший компонент Vue.js, а також дослідили багато концепцій, у тому числі генерацію шаблонного проекту з vue-cli, однофайлові компоненти, імпортування компонентів у компоненти, локальні стилі, директиви, обробники подій, обчислені властивості, спеціальні методи, односторонній потік даних, властивості та перевірку властивостей. І це лише маленька частина того, що може запропонувати Vue.js!

Помітили помилку? Повідомте автору, для цього достатньо виділити текст з помилкою та натиснути Ctrl+Enter
Codeguida 5.8K
Приєднався: 8 місяців тому
Коментарі (0)

    Ще немає коментарів

Щоб залишити коментар необхідно авторизуватися.

Вхід / Реєстрація