Работа с компонентами в фреймворке Vue


Если мы посмотрим на какой-нибудь сайт, то можем выделить на нем некоторые блоки: хедер, контент, сайдбар, футер.

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

Во Vue такие блоки называются компонентами. В крупных приложениях разделение на компоненты становится обязательным условием для сохранения управляемости процесса разработки

Итак, давайте приступим к изучению компонентов.

Простой компонент
Во Vue компонент делается с помощью метода Vue.component. Этот метод первым параметром принимает название нашего компонента, а вторым - объект настроек:

Vue.component(название компонента, {
настройки
});
Все компоненты следует создавать до команды new Vue:

Vue.component(название компонента, {
настройки
});

let app = new Vue({
el: '#app',
});
Давайте сделаем компонент с названием my-component:

Vue.component('my-component', {
настройки
});

let app = new Vue({
el: '#app',
});
Давайте теперь добавим настройки нашему компоненту. Первая и самая главная настройка - это template. С ее помощью мы должны задать HTML код, который будет результатом работы нашего компонента.

Давайте добавим эту настройку:

Vue.component('my-component', {
template: '<div>Пользовательский компонент!</div>',
});

let app = new Vue({
el: '#app',
});
Обратите внимание на то, что результат работы Vue.component мы никуда не записываем. Это и не нужно, ведь с помощью Vue.component просто происходит регистрация нового компонента. И после регистрации мы можем вставить этот компонент в любое место нашего HTML кода нашего Vue приложения.

Сейчас наше приложение Vue размещается в <div id="app">. Наш компонент называется my-component. Мы можем вставить результат работы нашего компонента в любое место <div id="app"> следующим образом: <my-component></my-component> - то есть каждому компоненту соответствует тег с таким же названием.

Давайте сделаем это:

<div id="app">
<my-component></my-component>
</div>
Ну, а теперь соберем все вместе и запустим:

Vue.component('my-component', {
template: '<div>Пользовательский компонент!</div>',
});

let app = new Vue({
el: '#app',
});
<div id="app">
<my-component></my-component>
</div>
В результате выполнения этого кода вместо тега компонента <my-component></my-component> подставится результат работы этого компонента, то есть содержимое его настройки template.

Результат будет таким:

<div id="app">
<div class="task">Пользовательский компонент!</div>
</div>
Передача данных в компонент
Сейчас у нас есть основной компонент, который мы создали с помощью new Vue и дочерний компонент my-component.

Как вы хорошо знаете, у основного компонента есть настройка data, внутри которой хранятся данные. Однако, наш дочерний компонент my-component не имеет доступа к этим данным - и это правильно: родительский компонент должен четко сказать, какие данные он хочет передать дочернему, а какие - нет.

Давайте передадим данные в наш компонент my-component. Пусть, к примеру, у нас есть имя 'Вася'. Передадим это имя в компонент так, чтобы к нему можно было обратиться внутри нашего компонента через фигурные скобки, вот так: {{ name }}.

Для этого нужно сделать следующее: вместо <my-component> написать <my-component name="Вася">, вот так:

<div id="app">
<my-component name="Вася"></my-component>
</div>
Теперь внутри настройки template нашего компонента мы можем написать {{ name }} и вместо этого в результате подставится имя 'Вася'.

Сделаем это:

Vue.component('my-component', {
template: '<div>Привет, {{ name }}!</div>',
});

let app = new Vue({
el: '#app',
});
Однако, просто написать атрибут мало. Внутри самого компонента мы должны сказать о том, что снаружи ожидается передача данных.

Это делается с помощью настройки props, внутри которой следует писать названия атрибутов, с помощью которых разрешено передавать данные в компонент.

Атрибуты пишутся в виде массива. У нас пока есть только один атрибут - name, поэтому давайте укажем его:

Vue.component('my-component', {
props: ['name'],
template: '<div>Привет, {{ name }}!</div>',
});

let app = new Vue({
el: '#app',
});
Пришло время собрать все вместе и запустить. Сделаем это:

Vue.component('my-component', {
props: ['name'],
template: '<div>Привет, {{ name }}!</div>',
});

let app = new Vue({
el: '#app',
});
<div id="app">
<my-component name="Вася"></my-component>
</div>
Результатом выполнения этого кода будет следующий HTML:

<div id="app">
<div class="task">Привет, Вася!</div>
</div>
Несколько атрибутов
Как вы уже поняли, можно передавать несколько атрибутов. Давайте кроме имени передадим еще и фамилию:

<div id="app">
<my-component name="Вася" surname="Иванов"></my-component>
</div>
Vue.component('my-component', {
props: ['name', 'surname'],
template: '<div>Привет, {{ name }} {{ surname }}!</div>',
});

let app = new Vue({
el: '#app',
});
Результатом выполнения этого кода будет следующий HTML:

<div id="app">
<div class="task">Привет, Вася Иванов!</div>
</div>
Зачем нам компоненты?
Зачем нам вообще нужны компоненты, если тоже самое можно сделать и без них? Давайте попробуем разобраться на примере.

Пусть у нас есть компонент my-component, который мы уже сделали ранее:

Vue.component('my-component', {
props: ['name'],
template: '<div>Привет, {{ name }}!</div>',
});

let app = new Vue({
el: '#app',
});
Используем наш компонент несколько раз, передав в него разные имена:

<div id="app">
<my-component name="Коля"></my-component>
<my-component name="Вася"></my-component>
<my-component name="Петя"></my-component>
</div>
Давайте запустим:

Vue.component('my-component', {
props: ['name'],
template: '<div>Привет, {{ name }}!</div>',
});

let app = new Vue({
el: '#app',
});
<div id="app">
<my-component name="Коля"></my-component>
<my-component name="Вася"></my-component>
<my-component name="Петя"></my-component>
</div>
Результатом выполнения этого кода будет следующий HTML:

<div id="app">
<div class="task">Привет, Коля!</div>
<div>Привет, Вася!</div>
<div>Привет, Петя!</div>
</div>
То есть один и тот же компонент можно использовать несколько раз с различными параметрами.

Какое в этом преимущество: если мы захотим поправить вывод имени на экран - нам придется это сделать только в одном месте, тут: template: '<div>Привет, {{ name }}!</div>'.

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

Таким образом мы отделяем данные от их представления на экране и тем самым получаем более удобный в поддержке код.

В чем удобство: хотим поправить вывод данных - правим дочерний компонент и не боимся при этом, что где-то что-то сломаем.

Думаю, все удобство вам не до конца понятно - читайте дальше, вы потом прочувствуете все это на реальных проектах, когда мы займемся ими.

Передача свойств из data
Пусть у нас теперь 'Вася' хранится в свойстве value объекта data:

let app = new Vue({
el: '#app',
data: {
value: 'Вася',
}
});
Чтобы передать данные из свойства value нам нужно будет сделать не name="value", а v-bind:name="value":

<div id="app">
<my-component v-bind:name="value"></my-component>
</div>
Если написать просто name="value", то в компонент передастся строка 'value', а не содержимое свойства value.

Давайте совместим все вышеописанное и запустим:

Vue.component('my-component', {
props: ['name'],
template: '<div>Привет, {{ name }}!</div>',
});

let app = new Vue({
el: '#app',
data: {
value: 'Вася',
}
});
<div id="app">
<my-component v-bind:name="value"></my-component>
</div>
Результатом выполнения этого кода будет следующий HTML:

<div id="app">
<div class="task">Привет, Вася!</div>
</div>
Итак, данные из data родительского компонента можно передавать в дочерний через директиву v-bind.

Компоненты и циклы
Пусть теперь в свойстве values хранится массив имен:

let app = new Vue({
el: '#app',
data: {
values: ['Коля', 'Вася', 'Петя'], // массив имен
}
});
Давайте применим к свойству values директиву v-for и с ее помощью повторим наш компонент my-component в цикле:

<div id="app">
<my-component v-for="value in values"></my-component>
</div>
Так можно делать и при это переменная value становится доступна в атрибутах нашего компонента. Давайте передадим ее в компонент с помощью директивы v-bind:

<div id="app">
<my-component
v-for="(value, index) in values"
v-bind:name="value"
>
</my-component>
</div>
Есть еще один нюанс. Чтобы Vue проще было работать с компонентами, нам необходимо указать ключ через v-bind:key (подобное мы уже проходили при работе с обычными циклами):

<div id="app">
<my-component
v-for="(value, index) in values"
v-bind:name="value"
v-bind:key="index"
>
</my-component>
</div>
Итак, давайте напишем полный код и запустим его:

Vue.component('my-component', {
props: ['name'],
template: '<div>Привет, {{ name }}!</div>',
});

let app = new Vue({
el: '#app',
data: {
values: ['Коля', 'Вася', 'Петя'],
}
});
<div id="app">
<my-component
v-for="(value, index) in values"
v-bind:name="value"
v-bind:key="index"
>
</my-component>
</div>
Результатом выполнения этого кода будет следующий HTML:

<div id="app">
<div class="task">Привет, Коля!</div>
<div>Привет, Вася!</div>
<div>Привет, Петя!</div>
</div>
Итак, компоненты можно и нужно перебирать в цикле - это достаточно удобно.