[Перевод] Vue.js для начинающих, урок 8: компоненты
Сегодня, в восьмом уроке курса по Vue, состоится ваше первое знакомство с компонентами. Компоненты — это блоки кода, подходящие для многократного использования, которые могут включать в себя и описание внешнего вида частей приложения, и реализацию возможностей проекта. Они помогают программистам в создании модульной кодовой базы, которую удобно поддерживать.
→ Vue.js для начинающих, урок 1: экземпляр Vue
→ Vue.js для начинающих, урок 2: привязка атрибутов
→ Vue.js для начинающих, урок 3: условный рендеринг
→ Vue.js для начинающих, урок 4: рендеринг списков
→ Vue.js для начинающих, урок 5: обработка событий
→ Vue.js для начинающих, урок 6: привязка классов и стилей
→ Vue.js для начинающих, урок 7: вычисляемые свойства
Цель урока
Основная цель данного урока — создание нашего первого компонента и исследование механизмов передачи данных в компоненты.
Начальный вариант кода
Вот код файла index.html
, находящийся в теге , с которого мы начнём работу:
{{ title }}
In stock
Out of Stock
Shipping: {{ shipping }}
- {{ detail }}
Cart({{ cart }})
Вот код main.js
:
var app = new Vue({
el: '#app',
data: {
product: 'Socks',
brand: 'Vue Mastery',
selectedVariant: 0,
details: ['80% cotton', '20% polyester', 'Gender-neutral'],
variants: [
{
variantId: 2234,
variantColor: 'green',
variantImage: './assets/vmSocks-green.jpg',
variantQuantity: 10
},
{
variantId: 2235,
variantColor: 'blue',
variantImage: './assets/vmSocks-blue.jpg',
variantQuantity: 0
}
],
cart: 0,
},
methods: {
addToCart() {
this.cart += 1;
},
updateProduct(index) {
this.selectedVariant = index;
console.log(index);
}
},
computed: {
title() {
return this.brand + ' ' + this.product;
},
image() {
return this.variants[this.selectedVariant].variantImage;
},
inStock(){
return this.variants[this.selectedVariant].variantQuantity;
}
}
})
Задача
Нам не нужно, чтобы во Vue-приложении все данные, методы, вычисляемые свойства размещались бы в корневом экземпляре Vue. Со временем это приведёт к появлению кода, который будет очень тяжело поддерживать. Вместо этого нам хотелось бы разбить код на модульные части, с которыми будет проще работать, и которые сделают разработку более гибкой.
Решение задачи
Начнём с того, что возьмём существующий код и перенесём его в новый компонент.
Вот как в файле main.js
регистрируется компонент:
Vue.component('product', {})
Первый аргумент — это выбранное нами имя компонента. Второй — это объект с опциями, похожий на тот, который мы использовали при создании экземпляра Vue на прошлых занятиях.
В экземпляре Vue мы использовали свойство el
для организации его привязки к элементу DOM. В случае с компонентом используется свойство template
, которое определяет HTML-код компонента.
Опишем шаблон компонента в объекте с опциями:
Vue.component('product', {
template: `
… // Здесь будет весь HTML-код, который раньше был в элементе с классом product
`
})
Во Vue есть несколько способов создания шаблонов. Сейчас мы пользуемся шаблонным литералом, содержимое которого заключено в обратные кавычки.
Если окажется так, что код шаблона не будет размещаться в единственном корневом элементе, в таком, как элемент Например, следующий шаблон построен правильно, так как он представлен лишь одним элементом: Теперь, когда в шаблоне находится HTML-код, который раньше был в файле Дело в том, что компоненты часто создают, планируя использовать их многократно. Если у нас будет много компонентов Теперь, когда мы переместили код, связанный с товаром, в собственный компонент Если теперь заглянуть в инструменты разработчика Vue, там можно заметить наличие сущности Root и компонента Product. А теперь, просто чтобы продемонстрировать возможности многократного использования компонентов, давайте добавим в код Обратите внимание на то, что в дальнейшем мы будем работать с одним компонентом Пусть в корневом экземпляре Vue имеется описание неких данных. Эти данные указывают на то, является ли пользователь обладателем премиум-аккаунта. Код описания экземпляра Vue при этом может выглядеть так: Это означает, что нам нужно, чтобы компонент Как отправить данные, хранящиеся в свойстве Начнём работу с описания того, какие именно входные параметры ожидает получить компонент Далее, внесём в шаблон изменение, выводящее переданные объекту параметры. Выведя значение свойства User is premium: {{ premium }} Но мы пока ещё не передали параметр Доработаем код в Теперь входные параметры передаются компоненту. Поговорим о том, что именно мы только что сделали. Мы передаём компоненту входной параметр, или «пользовательский атрибут», называемый Теперь корневой экземпляр Vue может передать Вышеприведённый рисунок, а именно, надпись Теперь мы убедились в том, что изучаемый нами механизм передачи данных работает так, как ожидается. Если заглянуть в инструменты разработчика Vue, то окажется, что у компонента Сейчас, когда данные о том, обладает ли пользователь премиум-аккаунтом, попадают в компонент, давайте используем эти данные для того чтобы вывести на странице сведения о стоимости доставки. Не будем забывать о том, что если параметр Уберём из шаблона компонента код вывода значения параметра Shipping: {{ shipping }} Текст Замечательно! Теперь мы научились передавать данные от родительских сущностей дочерним и смогли воспользоваться этими данными в компоненте для управления стоимостью доставки товаров. Кстати, стоит отметить, что в дочерних компонентах не следует изменять их входные параметры. Вот решение задачи. Пользуетесь ли вы инструментами разработчика Vue? → Vue.js для начинающих, урок 1: экземпляр Vueproduct
, это приведёт к выводу такого сообщения об ошибке:
Component template should contain exactly one root element
Другими словами, шаблон компонента может возвращать только один элемент.Vue.component('product', {
template: `
I'm a single element!
`
})
А вот если в шаблоне содержится несколько одноуровневых элементов, воспользоваться им не получится. Вот пример неправильного шаблона: Vue.component('product', {
template: `
I'm a single element!
Not anymore
`
})
В результате оказывается, что если шаблон должен включать в себя множество элементов, например — набор элементов, заключённых в наш product
, эти элементы должны быть помещены во внешний элемент-контейнер. В результате в шаблоне будет лишь один корневой элемент.
index.html
, мы можем добавить в компонент данные, методы, вычисляемые свойства, которые раньше были в корневом экземпляре Vue: Vue.component('product', {
template: `
Как видите, структура этого компонента практически полностью совпадает со структурой экземпляра Vue, с которым мы работали раньше. А вы обратили внимание на то, что data
— это теперь не свойство, а метод объекта с опциями? Почему это так? product
, нам нужно обеспечить то, чтобы для каждого из них создавались бы собственные экземпляры сущности data
. Так как data
— это теперь функция, которая возвращает объект с данными, каждый компонент гарантированно получит собственный набор данных. Если бы сущность data
не была бы функцией, то каждый компонент product
, везде, где использовались бы такие компоненты, содержал бы одни и те же данные. А это противоречит идее многократного использования компонентов.product
, код описания корневого экземпляра Vue будет выглядеть так: var app = new Vue({
el: '#app'
})
Сейчас нам осталось лишь разместить компонент product
в коде файла index.html
. Это будет выглядеть так:
Если теперь перезагрузить страницу приложения — она примет прежний вид.
Страница приложения
Анализ приложения с помощью инструментов разработчика Vueindex.html
ещё пару компонентов product
. Собственно говоря, именно так организовано многократное использование компонентов. Код index.html
будет выглядеть так:
А на странице будет выведено три копии карточки товара.
Несколько карточек товара, выведенные на одной страницеproduct
, поэтому код index.html
будет выглядеть так: Задача
В приложениях часто нужно, чтобы компоненты принимали бы данные, входные параметры, от родительских сущностей. В данном случае родителем компонента product
является сам корневой экземпляр Vue.var app = new Vue({
el: '#app',
data: {
premium: true
}
})
Давайте решим, что премиум-пользователям полагается бесплатная доставка.product
выводил бы, в зависимости от того, что записано в свойство premium
корневого экземпляра Vue, разные сведения о стоимости доставки.premium
корневого экземпляра Vue, дочернему элементу, которым является компонент product
? Решение задачи
Во Vue, для передачи данных от родительских сущностей дочерним, применяется свойство объекта с опциями props
, описываемое у компонентов. Это объект с описанием входных параметров компонента, значения которых должны быть заданы на основе данных, получаемых от родительской сущности.product
. Для этого добавим в объект с опциями, используемый при его создании, соответствующее свойство: Vue.component('product', {
props: {
premium: {
type: Boolean,
required: true
}
},
// Тут будут описания данных, методов, вычисляемых свойств
})
Обратите внимание на то, что тут используются встроенные возможности Vue по проверке параметров, передаваемых компоненту. А именно, мы указываем то, что типом входного параметра premium
является Boolean
, и то, что этот параметр является обязательным, устанавливая required
в true
.premium
на странице, мы убедимся в правильности работы исследуемого нами механизма.
Пока всё идёт нормально. Компонент product
знает о том, что он будет получать необходимый для его работы параметр типа Boolean
. Мы подготовили место для вывода соответствующих данных.premium
компоненту. Сделать это можно с помощью пользовательского атрибута, который похож на «трубопровод», ведущий к компоненту, через который ему можно передавать входные параметры, и, в частности, premium
.index.html
:
Обновим страницу.
Вывод данных, переданных компонентуpremium
. Мы привязываем этот пользовательский атрибут, используя конструкцию, представленную двоеточием, к свойству premium
, которое хранится в данных нашего экземпляра Vue.premium
дочернему компоненту product
. Так как атрибут привязан к свойству premium
из данных экземпляра Vue, текущее значение premium
будет всегда передаваться компоненту product
.User is premium: true
, доказывает то, что всё сделано правильно.Product
теперь есть входной параметр premium
, хранящий значение true
.
Входной параметр компонентаpremium
установлен в значение true
, то пользователю полагается бесплатная доставка. Создадим новое вычисляемое свойство shipping
и воспользуемся в нём параметром premium
: shipping() {
if (this.premium) {
return "Free";
} else {
return 2.99
}
}
Если в параметре this.premium
хранится true
— вычисляемое свойство shipping
вернёт Free
. В противном случае оно вернёт 2.99
.premium
. Теперь элемент
, который присутствовал в коде, с которого мы сегодня начали работу, сможет вывести сведения о стоимости доставки.
Премиум-пользователь получает бесплатную доставкуShipping: Free
появляется на странице из-за того, что компоненту передан входной параметр premium
, установленный в значение true
.Практикум
Создайте новый компонент product-details
, который должен использовать входной параметр details
и отвечать за визуализацию той части карточки товара, которая раньше формировалась с использованием следующего кода:
Вот заготовка, которую вы можете использовать для решения этой задачи.Итоги
Сегодня состоялось ваше первое знакомство с компонентами Vue. Вот что вы узнали: data()
объекта с опциями.props
).
→ Vue.js для начинающих, урок 2: привязка атрибутов
→ Vue.js для начинающих, урок 3: условный рендеринг
→ Vue.js для начинающих, урок 4: рендеринг списков
→ Vue.js для начинающих, урок 5: обработка событий
→ Vue.js для начинающих, урок 6: привязка классов и стилей
→ Vue.js для начинающих, урок 7: вычисляемые свойства