В этом курсе мы займёмся разработкой страницы, представляющей собой карточку товара.
Сегодня мы предлагаем вашему вниманию перевод учебного курса по Vue.js для начинающих.
Оригинал курса на английском вышел на сайте vuemastery.com, мы подготовили для вас перевод на русский.
Ссылка на оригинал перевода: https://habr.com/ru/companies/ruvds/articles/509700/
Эта статья представляет собой адаптацию курса под vue версии 3. Оригиральный курс был написан под vue 2, который уже не поддерживается разработчиком.
Предполагается, что тот, кто решит освоить этот курс, обладает знаниями в области базовых веб-технологий: HTML, CSS и JavaScript.
Страница, разработкой которой мы будем заниматься
В этом уроке мы разберёмся с тем, как использовать Vue для вывода данных на веб-странице.
Мы начнём работу с очень простого HTML- и JavaScript-кода, расположенного в двух файлах.
Файл index.html:
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Product App</title>
</head>
<body>
<div id="app">
<h1>Product Name</h1>
</div>
<script src="main.js"></script>
</body>
</html>
Файл main.js:
var product = "Socks";
В этом курсе в качестве среды, в которой предлагается выполнять домашние задания, используется платформа codepen.io. Соответствующие заготовки оформлены в виде CodePen-проектов. Тем, кто проходит этот курс, рекомендуется самостоятельно запускать весь код, который они здесь встречают.
В интерфейсе CodePen есть три области для кода. Это, соответственно, поля HTML, CSS и JS. Код, введённый в полях CSS и JS, автоматически подключается к веб-странице, описанной в поле HTML. То есть — для того чтобы воссоздать в среде CodePen вышеприведённый пример нужно ввести в область HTML код, содержащийся в теге
файла index.html без последней строчки, подключающей main.js, а в область JS — код main.js.
Начало экспериментов в CodePen
Использовать CodePen для запуска кода примеров необязательно. Вы вполне можете использовать какой-нибудь другой онлайн-сервис, или можете обойтись локальной средой разработки, воссоздав у себя описываемые здесь файлы.
Нам нужен механизм, который позволяет взять значение, имеющееся в JavaScript-коде, например то, что
записано сейчас в переменную product, и поместить его в код веб-страницы, в тег
h1.
Решить эту задачу нам поможет фреймворк Vue.js. Вот официальное русскоязычное руководство по нему.
Первым шагом нашей работы с Vue будет подключение фреймворка к странице. Для этого внесём изменения в файл index.html, добавив в него, прямо над кодом подключения файла main.js, следующее:
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
Далее, в main.js, вводим следующий код, убрав из него объявление переменной product:
const app = Vue.createApp({
data: () => ({
product: "Socks"
})
});
app.mount('#app');
Теперь нужно связать DOM с данными экземпляра Vue. Делается это с использованием особой HTML-конструкции, с помощью синтаксиса Mustache, при применении которого используются двойные фигурные скобки:
<div id="app">
<h1>{{ product }}</h1>
</div>
JavaScript-выражение в фигурных скобках будет заменено на значение свойства product объекта
data.
Вот как будет выглядеть проект в CodePen в том случае, если всё работает так, как нужно.
Данные перенесены из JavaScript на HTML-страницу
Как видите, нам удалось перенести данные из JavaScript-кода на HTML-страницу. А теперь давайте разберёмся в том, что мы только что сделали.
Вот схема кода, с помощью которого создают экземпляр Vue:
const app = Vue.createApp({ /*...опции...*/ });
Экземпляр Vue — это корневая сущность приложения. Его создают, передавая конструктору Vue объект с опциями. Этот объект содержит различные свойства и методы, которые дают экземпляру Vue возможность хранить данные и выполнять какие-то действия.
Обратите внимание на команду, использованную после создания экземпляра Vue:
app.mount('#app');
С помощью этого метода мы подключаем экземпляр Vue к элементу нашей страницы. Благодаря этому мы создаём
связь между экземпляром Vue и соответствующей частью DOM. Другими словами, мы активируем Vue в элементе
<div> с идентификатором app.
В экземпляре Vue имеется место для хранения данных. Эти данные описывают с помощью свойства
data объекта с опциями:
data: ()=>({
product: "Socks"
})
К данным, хранящимся в экземпляре Vue, можно обратиться из элемента веб-страницы, к которому подключён экземпляр Vue.
Если нам нужно, чтобы значение свойства product вывелось бы там, где выводится текст заголовка
первого уровня, имя этого свойства можно поместить в двойные фигурные скобки в соответствующем теге:
<h1>{{ product }}</h1>
Фактически, речь идёт о том, что в двойных фигурных скобках находится JavaScript-выражение, результаты
вычисления которого фреймворк подставляет в тег <h1> в качестве текста.
Выражения позволяют использовать значения, хранящиеся в экземпляре Vue, а так же JavaScript-конструкции, применение которых позволяет создавать какие-то новые значения.
Когда Vue видит выражение {{ product }}, он понимает, что мы ссылаемся на данные, связанные с
экземпляром Vue, используя ключ product. Фреймворк заменяет имя ключа на соответствующее ему
значение. В данном случае это — Socks.
Как уже было сказано, в двойных фигурных скобках можно использовать различные JavaScript-конструкции. Вот несколько примеров:
{{ product + '?' }}
{{ firstName + ' ' + lastName }}
{{ message.split('').reverse().join('') }}
Причина, по которой Vue сразу же после загрузки страницы выводит в теге <h1> значение,
соответствующее свойству product, заключается в том, что Vue — это реактивный фреймворк.
Другими словами, данные экземпляра Vue связаны со всеми местами веб-страницы, в которых есть ссылки на эти
данные. В результате Vue может не только вывести данные в некоем месте страницы, но и обновить
соответствующий HTML-код в том случае, если данные, на которые он ссылается, будут изменены.
Для того чтобы это доказать, давайте откроем консоль инструментов разработчика браузера и изменим значение,
записанное в свойство product объекта app. Когда мы это сделаем, например, введя в
консоли app.product = 'Coat', изменится и текст, выводимый на странице.
Изменение значения свойства product приводит к изменению текста, выводимого на веб-странице
Видите, как легко это делается?
Добавьте к уже имеющимся в экземпляре Vue данным ключ description, содержащий текст
A pair of warm, fuzzy socks. Затем выведите значение этого ключа в элементе
<p>, который должен находиться ниже элемента <h1>.
Вот заготовка, которую вы можете использовать для решения этой задачи.
Поговорим о том, что мы сегодня изучили:
{{ }}, содержащие JavaScript-выражения.Во втором уроке речь пойдёт о привязке атрибутов, о подключении данных, хранящихся в экземпляре Vue, к атрибутам HTML-элементов.
<div id="app">
<div class="product">
<div class="product-image">
<img src="" />
</div>
<div class="product-info">
<h1>{{ product }}</h1>
</div>
</div>
</div>
Обратите внимание на тег <div> с классом product-image. Именно в нём
содержится элемент <img>, к которому мы хотим динамически привязать данные, необходимые
для вывода изображения.
Элемент <div> с классом product-info используется для вывода названия
товара.
Вот JavaScript-код, содержащийся в файле main.js:
const app = Vue.createApp({
data: ()=>({
product: "Socks",
image: "./assets/vmSocks-green.jpg"
})
});
app.mount('#app');
Обратите внимание на то, что в объекте data теперь имеется новое свойство image,
содержащее путь к изображению.
→ Здесь можно найти CSS-код, используемый в этом уроке.
Для подключения стиля к index.html нужно добавить в тег
следующее:<link rel="stylesheet" type="text/css" href="style.css">
Тут мы исходим из предположения о том, что файл со стилями имеет имя style.css и хранится в той же папке, что и index.html.
Здесь находится изображение, которое мы будем выводить на странице.
Нам нужно, чтобы на странице вывелось изображение. При этом мы хотим динамически управлять этим
изображением. То есть, нам нужна возможность, позволяющая менять путь к изображению, хранящийся в экземпляре
Vue, и тут же видеть результаты этих изменений на странице. Так как именно атрибут src элемента
<img> отвечает за то, какое изображение выведет элемент, нам нужно привязать некие данные
к этому атрибуту. Это и позволит нам динамически, основываясь на данных, хранящихся в экземпляре Vue, менять
изображение.
Когда мы говорим о привязке данных во Vue, смысл этого заключается в том, что место в шаблоне, в котором используются или выводятся данные, напрямую «подключено», или «связано» с источником данных, то есть — с соответствующим объектом, хранящимся в экземпляре Vue.
Другими словами, сущность источник данных, связана с сущностью, в которой эти данные используются,
с приёмником данных. В нашем случае источник данных — это экземпляр Vue, а приёмник — это атрибут
src элемента <img>.
Для того чтобы привязать значение свойства image из объекта с данными к свойству
src тега <img>, мы воспользуемся директивой Vue v-bind.
Перепишем код тега <img> из файла index.html:
<img v-bind:src="image" />
Когда Vue, обрабатывая страницу, видит такую конструкцию, фреймворк заменяет её на следующий HTML-код:
<img src="./assets/vmSocks-green.jpg" />
Если всё сделано правильно, то на странице будет выведено изображение.
Изображение зелёных носков выведено на странице
А если поменять значение свойства image объекта data, то соответствующим образом
изменится и значение атрибута src, что приведёт к выводу на странице нового изображения.
Предположим, нам надо заменить изображение зелёных носков на изображение синих. Для этого, учитывая то, что
путь к файлу с новым изображением выглядит как ./assets/vmSocks-blue.jpg (файл изображения
можно найти здесь),
достаточно привести код описания свойства image в объекте data к такому виду:
image: "./assets/vmSocks-blue.jpg"
Это приведёт к тому, что на странице появится изображение синих носков.
Изображение синих носков выведено на странице
v-bind¶
Директиву v-bind можно использовать не только с атрибутом src. Она может помочь
нам и в динамической настройке атрибута изображения alt.
Добавим в объект с опциями data новое свойство altText:
altText: "A pair of socks"
Привяжем соответствующие данные к атрибуту alt, приведя код элемента <img>
к такому виду:
<img v-bind:src="image" v-bind:alt="altText" />
Здесь, как и в случае с атрибутом src, используется конструкция, состоящая из
v-bind, двоеточия и имени атрибута (alt).
Теперь, если в данных экземпляра Vue изменятся свойства image или altText, в
соответствующие атрибуты элемента <img> попадут обновлённые данные. При этом связь
атрибутов элемента и данных, хранящихся в экземпляре Vue, не нарушится.
Этот приём постоянно используется при разработке Vue-приложений. Из-за этого существует сокращённый вариант
записи конструкции v-bind:имя_атрибута. Он выглядит как :имя_атрибута. Если
использовать этот приём при написании кода тега <img>, то получится следующее:
<img :src="image" />
Это просто и удобно. Данный приём улучшает чистоту кода.
Добавьте на страницу ссылку (элемент <a>) с текстом
More products like this. В объекте data создайте свойство link,
содержащее ссылку https://www.wildberries.ru/catalog/0/search.aspx?search=носки. Свяжите,
используя директиву v-bind, свойство link с атрибутом href элемента
<a>.
→ Вот заготовка, которую вы можете использовать для решения этой задачи.
Вот что мы сегодня изучили:
v-bind.:).На третьем уроке речь пойдёт об условном рендеринге. О том, как выводить что-либо на странице только в том случае, если выполняется какое-то условие.
Нам нужно, чтобы в карточке товара выводилась бы надпись, сообщающая посетителю о том, есть товар на
складе, или нет. Если товар на складе есть, должна выводиться надпись In Stock. Если его на
складе нет — надпись Out of Stock. Решение о выводе той или иной надписи должно приниматься на
основе данных, хранящихся в приложении.
Вот код, с которого мы начнём работу. Он, как обычно, находится в файле index.html, в теге
<body>:
<div id="app">
<div class="product">
<div class="product-image">
<img :src="image" />
</div>
<div class="product-info">
<h1>{{ product }}</h1>
</div>
</div>
</div>
В файле main.js, при настройке экземпляра Vue, будет применяться следующий объект с данными:
data: ()=>({
product: "Socks",
image: "./assets/vmSocks-green.jpg",
inStock: true
})
Обратите внимание на то, что в объект data добавлено новое свойство. Это — свойство
inStock, хранящее логическое значение true.
При разработке веб-приложений часто бывает нужно, чтобы элемент отображался бы на странице в зависимости от выполнения некоего условия. Например, если запасы товара закончились, в карточке товара нужно об этом сообщить.
Соответствующие сообщения планируется оформить в качестве элементов <p>. Это значит, что
где-то в index.html будут следующие элементы:
<p>In Stock</p>
<p>Out of Stock</p>
Наша задача заключается в том, чтобы вывести один из них в том случае, если товар на складе есть, а другой — в ситуации, когда товара на складе нет.
В Vue решение этой задачи выглядит просто и понятно.
Как вы уже знаете, данные, указывающие на наличие или отсутствие товара на складе, описаны в main.js, в
объекте data:
inStock: true
Для того чтобы указать системе на то, какой именно элемент
нужно рендерить, мы можем воспользоваться директивами v-if и v-else. Это значит, что в index.html попадёт следующее:
<p v-if="inStock">In Stock</p>
<p v-else>Out of Stock</p>
Если в inStock содержится истинное значение, выведется первый элемент <p>.
В противном случае будет выведен второй элемент. В нашем случае в inStock записано значение
true, поэтому выведется In Stock.
На складе есть запасы товара
Замечательно! Только что мы воспользовались механизмом условного рендеринга для вывода сведений о товаре. Задачу мы решили. Но не будем останавливаться на достигнутом и продолжим исследование условного рендеринга.
Наш механизм условного рендеринга, основанный на директивах v-if и v-else, можно
расширить, добавив в него ещё один уровень логики. Сделать это можно с помощью директивы
v-else-if. Для того чтобы это продемонстрировать, давайте немного усложним наш пример.
Предположим, что в объекте data, в main.js, имеются сведения о количестве товара. Они хранятся
в свойстве inventory:
inventory: 100
Анализируя это свойство с помощью JavaScript-выражений, заключённых в кавычки, мы можем сообщать посетителям страницы более точные сведения о товаре:
<p v-if="inventory > 10">In stock</p>
<p v-else-if="inventory <= 10 && inventory > 0">Almost sold out!</p>
<p v-else>Out of stock</p>
В данной ситуации на страницу выведется первый элемент <p>, так как соответствующее ему
выражение оказывается истинным.
Если некий элемент страницы нужно часто скрывать и отображать, это значит, что для реализации этого
механизма имеет смысл взглянуть на директиву v-show. Элемент с такой директивой всегда будет
присутствовать в DOM, но видимым он будет только в том случае, если условие, переданное директиве, окажется
истинным. Фактически, речь идёт о том, что, благодаря использованию этой директивы, к элементу, по условию,
будет применяться CSS-свойство display: none.
Этот метод отличается более высокой производительностью, чем управление элементами с использованием
v-if и v-else.
Вот как выглядит применение этой директивы:
<p v-show="inStock">In Stock</p>
Тот вариант решения нашей задачи, в котором использовались директивы v-if и
v-else, нас устраивает. Поэтому мы остановимся на нём и не будем ничего менять.
Добавьте в объект с данными свойство onSale. Оно должно использоваться для управления
рендерингом элемента <span>, выводящего текст On Sale и сообщающего
посетителям о распродаже.
Вот заготовка, которую вы можете использовать для решения этой задачи.
Сегодня вы узнали об условном рендеринге с использованием механизмов Vue. А именно, речь шла о следующем:
v-ifv-else-ifv-elsev-showv-show влияет только на видимость элемента, она не вставляет элементы в DOM и не
удаляет элементы из DOM.Сегодня, в четвёртом уроке учебного курса по Vue, мы поговорим о том, как выводить на страницу списки элементов.
<div class="product">
<div class="product-image">
<img :src="image" />
</div>
<div class="product-info">
<h1>{{ product }}</h1>
<p v-if="inStock">In stock</p>
<p v-else>Out of Stock</p>
</div>
</div>
Вот как будет выглядеть объект data, используемый при создании экземпляра Vue в main.js:
data: ()=>({
product: "Socks",
image: "./assets/vmSocks-green.jpg",
inStock: true,
details: ['80% cotton', '20% polyester', 'Gender-neutral']
})
Здесь появилось новое свойство — массив details.
Необходимо вывести на странице содержимое массива details. Для этого требуется найти ответы на
вопросы о том, как перебрать массив, и о том, как визуализировать его данные.
details: ['80% cotton', '20% polyester', 'Gender-neutral']
Тут нам поможет ещё одна директива Vue — v-for. Она позволяет перебирать массивы и выводить
содержащиеся в них данные.
Добавим в index.html следующий код:
<ul>
<li v-for="detail in details">{{ detail }}</li>
</ul>
Благодаря этому на странице появится список дополнительных сведений о товаре.
Список на странице
Синтаксическая конструкция, используемая в кавычках вместе с директивой v-for, покажется
знакомой тем, кто пользовался JavaScript-циклами for of или for in. Поговорим о
том, как работает директива v-for.
Здесь мы используем существительное в единственном числе (detail) в качестве псевдонима для
строковых значений, извлекаемых из массива. Затем мы пишем in и указываем имя коллекции,
которую перебираем (details). В двойных фигурных скобках указывается то, какие именно данные мы
хотим выводить ({{ detail }}).
Так как конструкция v-for находится внутри элемента <li>, Vue выведет новый
элемент <li> для каждого элемента массива details. Если бы директива
v-for использовалась внутри элемента <div>, тогда для каждого элемента
массива выводился бы элемент <div>, визуализирующий значение этого элемента массива.
Директиву v-for можно представить себе в виде конвейера, на котором имеется манипулятор. Он
берёт элементы коллекции, по одному за раз, и собирает список.
Директива v-for похожа на конвейер
Рассмотрим ещё один пример применения v-for, более сложный. Здесь мы будем выводить в элементе
<div> данные, хранящиеся в массиве объектов.
Карточка товара, разработкой которой мы занимаемся, нуждается в возможности выводить сведения о разных
вариантах одного и того же товара. Эти сведения содержатся в массиве объектов variants, который
хранится в объекте с данными data. Как перебрать этот массив объектов для вывода данных?
Вот массив, о котором идёт речь:
variants: [
{
variantId: 2234,
variantColor: 'green'
},
{
variantId: 2235,
variantColor: 'blue'
}
]
В объектах, которые содержатся в данном массиве, имеется название цвета и идентификатор варианта товара.
Выведем эти данные на странице:
<div v-for="variant in variants">
<p>{{ variant.variantColor }}</p>
</div>
Список вариантов товара
Здесь нам нужно вывести на страницу лишь название цвета, соответствующее разным вариантам товара. Поэтому
мы, обращаясь к элементам массива, используем точечную нотацию. Если бы мы, в фигурных скобках, написали
{{ variant }}, то на страницу вывелся бы весь объект.
Обратите внимание на то, что при рендеринге подобных элементов рекомендуется использовать специальный
атрибут key. Это позволяет Vue отслеживать идентичность элементов. Добавим такой атрибут в наш
код, используя в качестве его значения уникальное свойство variantId объектов, содержащих
сведения о вариантах товара:
<div v-for="variant in variants" :key="variant.variantId">
<p>{{ variant.variantColor }}</p>
</div>
Добавьте в объект с данными массив sizes, содержащий сведения о размерах носков, и, используя
директиву v-for, выведите данные из этого массива на странице в виде списка.
Массив sizes может выглядеть так:
sizes: ['S', 'M', 'L', 'XL', 'XXL', 'XXXL']
Вот заготовка, которую вы можете использовать для решения этой задачи.
Сегодня мы узнали следующее:
v-for позволяет перебирать массивы для вывода содержащихся в них данных.v-for для доступа к элементам массива используется псевдоним. Здесь же
указывается и имя самого массива. Например, это может выглядеть так: v-for='item in items'.
v-for рекомендуется назначать каждому выводимому элементу уникальный
ключ.Сегодня, в пятом уроке курса по Vue.js для начинающих, речь пойдёт о том, как обрабатывать события.
Первая цель урока заключается в том, чтобы в карточке товара появилась бы кнопка, нажатия на которую увеличивают количество товара в корзине.
Вторая цель заключается в том, чтобы при наведении мыши на названия цветов вариантов товара менялось бы изображение товара.
В файле проекта index.html будет присутствовать следующий код:
<div id="app">
<div class="product">
<div class="product-image">
<img :src="image" />
</div>
<div class="product-info">
<h1>{{ product }}</h1>
<p v-if="inStock">In stock</p>
<p v-else>Out of Stock</p>
<ul>
<li v-for="detail in details">{{ detail }}</li>
</ul>
<div v-for="variant in variants" :key="variant.variantId">
<p>{{ variant.variantColor }}</p>
</div>
</div>
</div>
</div>
Вот содержимое main.js:
const app = Vue.createApp({
data: () => ({
product: "Socks",
image: "./assets/vmSocks-green.jpg",
inStock: true,
details: ['80% cotton', '20% polyester', 'Gender-neutral'],
variants: [
{
variantId: 2234,
variantColor: "green"
},
{
variantId: 2235,
variantColor: "blue"
}
]
})
});
app.mount('#app');
Нам нужна кнопка, которой будет назначен прослушиватель события, реагирующий на щелчок по ней. По щелчку должен запускаться метод, который и выполняет увеличение количества товара в корзине.
Для начала — добавим, в main.js, в объект data, новое свойство, которое будет символизировать
количество товара в корзине:
cart: 0
Теперь, в index.html, добавим элемент <div>, описывающий корзину. В этом элементе будет
использован тег <p>, с помощью которого на страницу будет выводиться число, хранящееся в
свойстве cart:
<div class="cart">
<p>Cart({{ cart }})</p>
</div>
Ещё мы создадим в коде index.html кнопку, которая позволяет добавлять товар в корзину:
<button v-on:click="cart += 1">Add to cart</button>
Здесь обратите внимание на то, что для инкрементирования значения, хранящегося в cart, мы
используем директиву v-on.
Страница с корзиной и с кнопкой для добавления товара в корзину
Если теперь нажать на кнопку — количество товара в корзине увеличится на 1.
Как всё это работает?
Давайте разберёмся в представленной здесь конструкции. Использование директивы v-on сообщает
Vue о том, что мы хотим прослушивать события, происходящие с кнопкой. Потом идёт двоеточие, после которого
указывается то, какое конкретно событие нас интересует. В данном случае это — событие click. В
кавычках записано выражение, которое добавляет 1 к значению, хранящемуся в cart. Это происходит
при каждом щелчке по кнопке.
Это — простой, но не вполне реалистичный пример. Вместо того, чтобы указывать в кавычках выражение
cart += 1, давайте сделаем так, чтобы щелчок по кнопке вызывал бы метод, который будет
увеличивать значение, хранящееся в cart. Вот как это выглядит:
<button v-on:click="addToCart">Add to cart</button>
Как видите, здесь addToCart — это имя метода, который будет вызван при возникновении события
click. Но сам метод мы пока не объявили, поэтому давайте сделаем это прямо сейчас, оснастив им
наш экземпляр Vue.
Тут используется механизм, очень похожий на тот, который мы уже применяем для хранения данных. А именно,
речь идёт о том, что у объекта с опциями, используемого при создании экземпляра Vue, может быть
необязательное свойство, носящее имя methods, в котором содержится объект с методами. В нашем
случае это будет всего один метод — addToCart:
methods: {
addToCart() {
this.cart += 1
}
}
Теперь, когда мы щёлкаем по кнопке, вызывается метод addToCart, который и увеличивает значение
cart, выводящееся в теге <p>.
Продолжим разбор того, что здесь происходит.
Кнопка прослушивает события click благодаря директиве v-on, которая вызывает
метод addToCart. Этот метод находится в свойстве methods экземпляра Vue. В теле
функции содержится инструкция, добавляющая 1 к значению this.cart. Так как this
хранит ссылку на то место, где хранятся данные экземпляра Vue, в котором мы находимся, функция добавляет 1 к
значению cart. А this.cart — это то же самое, что и свойство cart,
объявленное в свойстве data объекта с опциями.
Если бы мы просто написали бы в теле функции что-то вроде cart += 1, то мы столкнулись бы с
сообщением об ошибке cart is not defined. Именно поэтому мы используем конструкцию
this.cart и обращаемся к cart из экземпляра Vue, используя this.
Возможно, вы сейчас задаётесь вопросом о том, что сейчас мы просто увеличиваем количество товаров в корзине, но самого товара в корзину не добавляем. Может, мы что-то делаем не так? Это — правильный вопрос. Мы реализуем соответствующий функционал позже, в одном из следующих уроков.
Итак, теперь, когда мы изучили основы обработки событий во Vue, взглянем на более сложный пример.
Для начала — давайте расширим объекты массива variants из объекта data, добавив
туда свойство variantImage, хранящее путь к изображению нужного варианта товара. Приведём
соответствующий раздел файла main.js к такому виду:
variants: [
{
variantId: 2234,
variantColor: "green",
variantImage: "./assets/vmSocks-green.jpg"
},
{
variantId: 2235,
variantColor: "blue",
variantImage: "./assets/vmSocks-blue.jpg"
}
],
Теперь каждому варианту товара, зелёным и синим носкам, назначено собственное изображение.
Нужно, чтобы, по наведению мыши на название цвета варианта носков, в поле, где выводится изображение
товара, вывелось бы изображение variantImage для соответствующего цвета.
Тут нам снова пригодится директива v-on. Но в этот раз мы воспользуемся сокращённым вариантом
её записи, который выглядит как @. А прослушивать будем событие mouseover.
Вот соответствующий код в index.html:
<div v-for="variant in variants" :key="variant.variantId">
<p @mouseover="updateProduct(variant.variantImage)">
{{ variant.variantColor }}
</p>
</div>
Обратите внимание на то, что мы передаём методу updateProduct, в виде аргумента,
variant.variantImage.
Создадим этот метод в main.js:
updateProduct(variantImage) {
this.image = variantImage
}
Этот метод очень похож на тот, который мы недавно создавали для увеличения значения cart.
Но тут мы обновляем значение, хранящееся в image. А именно, в image записывается
то, что хранится в variantImage того варианта товара, на который наведён указатель мыши.
Соответствующее значение передаётся функции updateProduct из самого обработчика события,
находящегося в index.html:
<p @mouseover="updateProduct(variant.variantImage)">
Другими словами, теперь метод updateProduct готов к вызову с параметром
variantImage.
Когда вызывается этот метод, variant.variantImage передаётся ему в виде
variantImage и используется для обновления значения, хранящегося в this.image. Мы,
по аналогии с ранее рассмотренной конструкцией this.cart, можем сказать, что
this.image — это то же самое, что image. В результате значение, хранящееся в
image, теперь динамически обновляется в соответствии с данными варианта товара, на который
наведён указатель мыши.
Здесь мы, создавая методы, пользовались такими конструкциями:
updateProduct(variantImage) {
this.image = variantImage
}
Это сокращённый вариант описания методов, который появился в ES6. Более старый вариант записи подобных конструкций выглядит так:
updateProduct: function(variantImage) {
this.image = variantImage
}
Создайте кнопку и соответствующий метод, которые позволят уменьшать значение, хранящееся в
cart.
→ Вот заготовка, которую вы можете использовать для решения этой задачи.
Подведём итоги сегодняшнего занятия:
v-on.v-on выглядит как @.clickmouseoverv-on может вызывать методы.v-on, может принимать аргументы.this содержит ссылку на то место, где хранятся данные текущего экземпляра
Vue. Его использование позволяет работать с данными экземпляра, а так же с методами, объявленными в
экземпляре.Сегодня, в шестом уроке курса по Vue, мы поговорим о том, как динамически стилизовать HTML-элементы, привязывая данные к их атрибутам style и привязывая к элементам классы.
Первой целью данного урока будет использование цвета, соответствующего вариантам товаров, для настройки
свойства background-color элементов <div>, выводящих сведения об этих
вариантах. Так как вариантам товара соответствуют цвета green и blue, нам нужно,
чтобы один элемент <div> имел бы зелёный фоновый цвет, а второй — синий.
Вторая цель заключается в том, чтобы, используя привязку классов, отключать, по некоему условию, ненужные элементы управления.
Вот как выглядит сейчас код, находящийся в index.html:
<div id="app">
<div class="product">
<div class="product-image">
<img :src="image" />
</div>
<div class="product-info">
<h1>{{ product }}</h1>
<p v-if="inStock">In stock</p>
<p v-else>Out of Stock</p>
<ul>
<li v-for="detail in details">{{ detail }}</li>
</ul>
<div v-for="variant in variants" :key="variant.variantId">
<p @mouseover="updateProduct(variant.variantImage)">
{{ variant.variantColor }}
</p>
</div>
<button v-on:click="addToCart">Add to cart</button>
<div class="cart">
<p>Cart({{ cart }})</p>
</div>
</div>
</div>
</div>
Вот что сейчас находится в main.js:
const app = createApp({
data() {
return {
product: "Socks",
image: "./assets/vmSocks-green.jpg",
inStock: true,
details: ['80% cotton', '20% polyester', 'Gender-neutral'],
variants: [
{
variantId: 2234,
variantColor: "green",
variantImage: "./assets/vmSocks-green.jpg"
},
{
variantId: 2235,
variantColor: "blue",
variantImage: "./assets/vmSocks-blue.jpg"
}
],
cart: 0
};
},
methods: {
addToCart() {
this.cart += 1;
},
updateProduct(variantImage) {
this.image = variantImage;
}
}
});
app.mount('#app');
В предыдущем уроке мы создали обработчик событий, который меняет изображение товара, основываясь на том, на
какой элемент <p> был наведён указатель мыши. Вместо того чтобы выводить название цвета в
элементе <p>, мы хотели бы использовать этот цвет для настройки свойства
background-color соответствующего элемента <div>. При таком подходе, вместо
того, чтобы наводить мышь на тексты, мы сможем наводить её на цветные квадраты, что приведёт к выводу на
странице изображения товара, цвет которого соответствует цвету, показанному в квадрате.
Для начала — давайте добавим к элементу <div> класс color-box, который
задаёт его ширину, высоту и внешний верхний отступ. Так как мы, даже сделав это, продолжаем выводить в
элементах <div> слова green и blue, мы можем взять названия
цветов, хранящихся в объектах, описывающих варианты товара, и использовать эти названия при привязке стиля к
атрибуту style. Вот как это выглядит:
<div
class="color-box"
v-for="variant in variants"
:key="variant.variantId"
:style="{ backgroundColor:variant.variantColor }"
>
<p @mouseover="updateProduct(variant.variantImage)">
{{ variant.variantColor }}
</p>
</div>
Обратите внимание на вторую и пятую строки этого кода. Здесь мы добавляем к элементу
<div> класс color-box и привязываем к нему встроенный стиль. Встроенный
стиль здесь используется для динамической настройки свойства background-color элементов
<div>. Цвет для фона элементов берётся из variant.variantColor.
Стилизованные элементы <div> и выводимые на них надписи
Теперь, когда элемент <div> стилизован с использованием variantColor, нам
больше не нужно выводить в нём название цвета. Поэтому мы можем избавиться от тега <p> и
переместить конструкцию @mouseover="updateProduct(variant.variantImage)" в сам
элемент <div>.
Вот как будет выглядеть код после внесения в него вышеописанных изменений:
<div
class="color-box"
v-for="variant in variants"
:key="variant.variantId"
:style="{ backgroundColor:variant.variantColor }"
@mouseover="updateProduct(variant.variantImage)"
>
</div>
Стилизованные элементы <div> без текста
Теперь при наведении мыши на синий квадрат на странице выводится изображение синих носков. А при наведении мыши на зелёный квадрат — изображение зелёных носков. Красота!
Разобравшись с привязкой стилей, поговорим о привязке классов.
Сейчас в наших данных есть следующее:
inStock: true,
Когда свойство inStock принимает значение false, нам нужно запретить посетителям
сайта щёлкать по кнопке Add to Cart, так как на складе нет товара, а значит, его нельзя
добавить в корзину. К нашей удаче, существует специальный HTML-атрибут, носящий имя disabled, с
помощью которого можно отключить кнопку.
Если вспомнить материал второго урока, то окажется, что мы можем воспользоваться техникой привязки
атрибутов для добавления к элементу атрибута disabled тогда, когда inStock
равняется false, или, скорее, в случае, когда это значение не является истинным
(!inStock). Перепишем код кнопки:
<button
v-on:click="addToCart"
:disabled="!inStock"
>
Add to cart
</button>
Теперь, в том случае, если в inStock записано false, кнопка работать не будет. Но
её внешний вид не изменится. Другими словами, кнопка всё ещё будет выглядеть так, будто на неё можно нажать,
несмотря на то, что на самом деле нажимать на неё бессмысленно.
Отключённая кнопка выглядит так же, как обычная, но щёлкать по ней бессмысленно
Тут мы поступим, действуя по той же схеме, по которой действовали, привязывая inStock к
атрибуту disabled. А именно, будем привязывать класс disabledButton к нашей кнопке
в случаях, когда inStock хранит false. При таком подходе, если по кнопке будет
бессмысленно щёлкать, то и выглядеть она будет соответственно.
<button
v-on:click="addToCart"
:disabled="!inStock"
:class="{ disabledButton: !inStock }"
>
Add to cart
</button>
Отключённая кнопка выглядит так, как нужно
Как видите, теперь кнопка становится серой в том случае, если inStock равняется
false.
Давайте разберёмся в том, что здесь происходит.
Взгляните на эту строчку:
:class="{ disabledButton: !inStock }"
Здесь мы используем сокращённый вариант записи директивы v-bind (:) для организации привязки
данных к атрибуту class кнопки. В фигурных скобках мы определяем присутствие класса
disabledButton на основании истинности свойства inStock.
Другими словами, когда товара на складе нет (!inStock), к кнопке добавляется класс
disabledButton. Так как этот класс задаёт серый фоновый цвет кнопки, кнопка становится серой.
Замечательно! Только что мы скомбинировали наши новые знания, касающиеся привязки классов, со знаниями о
привязке атрибутов, и смогли отключить кнопку и сделать её серой в том случае, если inStock
равняется false.
К элементу можно привязывать объект классов или массив классов:
<div :class="classObject"></div>
<div :class="[activeClass, errorClass]"></div>
Когда в inStock записано значение false, нужно привязать к тегу
<p>, выводящему текст Out of Stock, класс, который добавляет к элементу
стиль text-decoration: line-through, перечёркивая текст.
→ Вот заготовка, которую вы можете использовать для решения этой задачи.
Вот самое важное из того, что мы сегодня изучили:
style.class.Сегодня, в седьмом уроке курса по Vue, мы поговорим о вычисляемых свойствах. Эти свойства экземпляра Vue не хранят значения, а вычисляют их.
<div id="app">
<div class="product">
<div class="product-image">
<img :src="image" />
</div>
<div class="product-info">
<h1>{{ product }}</h1>
<p v-if="inStock">In stock</p>
<p v-else :class="{ outOfStock: !inStock }">Out of Stock</p>
<ul>
<li v-for="detail in details">{{ detail }}</li>
</ul>
<div
class="color-box"
v-for="variant in variants"
:key="variant.variantId"
:style="{ backgroundColor:variant.variantColor }"
@mouseover="updateProduct(variant.variantImage)"
></div>
<button
v-on:click="addToCart"
:disabled="!inStock"
:class="{ disabledButton: !inStock }"
>
Add to cart
</button>
<div class="cart">
<p>Cart({{ cart }})</p>
</div>
</div>
</div>
</div>
Вот код main.js:
const app = createApp({
data() {
return {
product: 'Socks',
brand: 'Vue Mastery',
image: './assets/vmSocks-green.jpg',
inStock: true,
details: ['80% cotton', '20% polyester', 'Gender-neutral'],
variants: [
{
variantId: 2234,
variantColor: 'green',
variantImage: './assets/vmSocks-green.jpg'
},
{
variantId: 2235,
variantColor: 'blue',
variantImage: './assets/vmSocks-blue.jpg'
}
],
cart: 0
};
},
methods: {
addToCart() {
this.cart += 1;
},
updateProduct(variantImage) {
this.image = variantImage;
}
}
});
app.mount('#app');
Обратите внимание на то, что в объект с данными добавлено новое свойство с именем brand.
Нам надо, чтобы то, что хранится в brand и в product, было бы скомбинировано в
одну строку. Другими словами, нам нужно вывести в теге <h1> текст
Vue Mastery Socks, а не просто Socks. Для решения этой задачи нужно задаться
вопросом о том, как можно конкатенировать два строковых значения, хранящихся в экземпляре Vue.
Мы для решения этой задачи воспользуемся вычисляемыми свойствами. Так как эти свойства не хранят значения,
а вычисляют их, давайте добавим в объект с опциями, используемый при создании экземпляра Vue, свойство
computed и создадим вычисляемое свойство с именем title:
computed: {
title() {
return this.brand + ' ' + this.product;
}
}
Полагаем, тут всё устроено очень просто и понятно. Когда вызывается метод title(), он
выполняет конкатенацию строк brand и product, после чего возвращает полученную в
результате новую строку.
Теперь нам осталось лишь вывести title в теге <h1> нашей страницы.
Сейчас этот тег выглядит так:
<h1>{{ product }}</h1>
А теперь мы сделаем его таким:
<h1>{{ title }}</h1>
Взглянем на страницу и проверим работоспособность того, что мы только что сделали.
Заголовок страницы изменился
Как видно, в заголовке выводится Vue Mastery Socks, а это значит, что мы всё сделали
правильно.
Мы взяли два значения из данных экземпляра Vue и создали на их основе новое значение. Если значение
brand когда-нибудь будет обновлено, например — в это свойство окажется записанной строка
Vue Craftery, то вносить какие-то изменения в код вычисляемого свойства не потребуется. Это
свойство будет продолжать возвращать корректную строку, которая теперь будет выглядеть как
Vue Craftery Socks. В вычисляемом свойстве title всё ещё будет использоваться
свойство brand, так же, как и раньше, но теперь в brand будет записано новое
значение.
Это был очень простой пример, но пример, вполне применимый на практике. Давайте теперь рассмотрим более сложный вариант использования вычисляемых свойств.
Сейчас мы обновляем изображение, выводимое на странице, используя метод updateProduct. Мы
передаём ему variantImage, а затем записываем в свойство image то, что попало в
метод после наведения мыши на соответствующий цветной квадрат. Соответствующий код выглядит так:
updateProduct(variantImage) {
this.image = variantImage;
}
Этот механизм работает нормально, но если нам понадобится, основываясь на том, на какой именно цветной квадрат наведена мышь, менять не только изображение, но и что-то ещё, это будет означать необходимость рефакторинга данного кода. Давайте этим и займёмся.
А именно, вместо того, чтобы хранить в данных свойство image, заменим его на свойство
selectedVariant. Инициализируем его значением 0.
selectedVariant: 0,
Почему 0? Дело в том, что мы планируем устанавливать это свойство на основе индекса (index)
элемента, над которым находится указатель мыши. Мы можем добавить индекс в конструкцию v-for:
<div
class="color-box"
v-for="(variant, index) in variants"
:key="variant.variantId"
:style="{ backgroundColor:variant.variantColor }"
@mouseover="updateProduct(variant.variantImage)"
></div>
Обратите внимание на то, что там, где раньше была конструкция
v-for="variant in variants", теперь находится код
v-for="(variant, index) in variants".
Теперь, вместо того, чтобы передавать variant.variantImage в updateProduct,
передадим в этот метод index:
@mouseover="updateProduct(index)"
Теперь займёмся кодом метода updateProduct. Здесь мы получаем индекс. И, вместо записи нового
значения в this.image, запишем index в this.selectedVariant. То есть,
в selectedVariant попадёт значение index, соответствующее тому квадрату, на
который был наведён указатель мыши. Ещё мы, в целях отладки, поместим в этот метод команду для логирования
значения index.
updateProduct(index) {
this.selectedVariant = index;
console.log(index);
}
Если сейчас обновить страницу и открыть консоль инструментов разработчика, мы можем убедиться в том, что при наведении мыши на квадраты в консоль попадают значения 0 и 1.
Проверка работоспособности созданного нами механизма
Однако изображение теперь на странице не выводится. В консоли появляется предупреждение.
Предупреждение, выводимое в консоль
Дело тут в том, что мы удалили свойство image, заменив его свойством
selectedValue, но это свойство в нашем приложении всё ещё используется. Давайте исправим
проблему, вернув image в экземпляр Vue, но на этот раз — в виде вычисляемого свойства.
Соответствующий код будет выглядеть так:
image() {
return this.variants[this.selectedVariant].variantImage;
}
Здесь мы возвращаем свойство variantImage элемента массива
this.variants[this.selectedVariant]. В качестве индекса, по которому осуществляется доступ к
элементу массива, используется свойство this.selectedVariant, которое равняется 0 или 1. Это,
соответственно, даёт нам доступ к первому или ко второму элементу массива.
Если теперь обновить страницу, изображение выведется и будет реагировать на наведение мыши на цветные квадраты. Но теперь этот механизм реализован с использованием вычисляемого свойства.
Сейчас, когда мы подвергли рефакторингу код метода updateProduct, который теперь обновляет
состояние свойства selectedVariant, мы можем поработать и с другими данными, хранящимися в
объектах из массива variants, с такими, как поле variantQuantity, которое мы
сейчас добавим в объекты:
variants: [
{
variantId: 2234,
variantColor: 'green',
variantImage: './assets/vmSocks-green.jpg',
variantQuantity: 10
},
{
variantId: 2235,
variantColor: 'blue',
variantImage: './assets/vmSocks-blue.jpg',
variantQuantity: 0
}
],
Давайте избавимся от обычного свойства inStock и, как и при работе со свойством
image, создадим новое вычисляемое свойство с тем же именем, значение, возвращаемое которым,
будет основываться на selectedVariant и variantQuantity:
inStock(){
return this.variants[this.selectedVariant].variantQuantity
}
Это свойство очень похоже на вычисляемое свойство image. Но теперь мы берём из
соответствующего объекта не свойство variantImage, а свойство variantQuantity.
Если теперь навести указатель мыши на квадрат, количество товара, соответствующее которому, равняется нулю,
в inStock попадёт 0, а 0 является в JavaScript значением, приводимым к логическому значению
false. Из-за этого на странице будет выведено сообщение Out of Stock.
Обратите внимание на то, что кнопка тоже, как и ранее, правильно реагирует на установку
inStock в 0.
Кнопка и надпись зависят от количества товара каждого вида
Почему всё продолжает правильно работать? Дело в том, что inStock всё ещё используется для
привязки класса disableButton к нашей кнопке. Единственное различие нового варианта приложения
и его предыдущего варианта заключается в том, что теперь inStock — это вычисляемое, а не
обычное свойство.
Вычисляемые свойства кешируются. То есть — результаты вычисления этих свойств сохраняются в системе до тех
пор, пока не изменятся данные, от которых зависят эти результаты. В результате, когда изменится
variantQuantity, кеш будет очищен. А когда к inStock обратятся в следующий раз,
свойство вернёт новый результат, который и будет помещён в кеш.
Учитывая это, можно сказать, что если для получения некоего значения требуются ресурсоёмкие вычисления, то для их выполнения выгоднее использовать вычисляемое свойство, а не метод. Метод придётся вызывать каждый раз, когда будет нужно соответствующее значение.
Кроме того, важно помнить о том, что в коде вычисляемых свойств не следует менять данные, хранящиеся в экземпляре Vue. В этом коде нужно лишь выполнять вычисления, основанные на существующих данных. Эти функции должны быть чистыми, лишёнными побочных эффектов.
Добавьте в объект с данными, используемый при создании экземпляра Vue, новое логическое свойства
onSale. Оно будет указывать на то, проводится ли распродажа. Создайте вычисляемое свойство
sale, которое, на основе brand, product и onSale
формирует строку, сообщающую о том, проводится ли сейчас распродажа или нет. Выведите эту строку в карточке
товара.
→ Вот заготовка, которую вы можете использовать для решения этой задачи
На этом занятии мы познакомились с вычисляемыми свойствами. Вот самое важное из того, что мы о них узнали:
Сегодня, в восьмом уроке курса по Vue, состоится ваше первое знакомство с компонентами. Компоненты — это блоки кода, подходящие для многократного использования, которые могут включать в себя и описание внешнего вида частей приложения, и реализацию возможностей проекта. Они помогают программистам в создании модульной кодовой базы, которую удобно поддерживать.
<div id="app">
<div class="product">
<div class="product-image">
<img :src="image" />
</div>
<div class="product-info">
<h1>{{ title }}</h1>
<p v-if="inStock">In stock</p>
<p v-else>Out of Stock</p>
<p>Shipping: {{ shipping }}</p>
<ul>
<li v-for="detail in details">{{ detail }}</li>
</ul>
<div
class="color-box"
v-for="(variant, index) in variants"
:key="variant.variantId"
:style="{ backgroundColor: variant.variantColor }"
@mouseover="updateProduct(index)"
></div>
<button
v-on:click="addToCart"
:disabled="!inStock"
:class="{ disabledButton: !inStock }"
>
Add to cart
</button>
<div class="cart">
<p>Cart({{ cart }})</p>
</div>
</div>
</div>
</div>
Вот код main.js:
const app = Vue.createApp({
data() {
return {
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;
}
}
});
app.mount('#app');
Нам не нужно, чтобы во Vue-приложении все данные, методы, вычисляемые свойства размещались в корневом экземпляре Vue. Со временем это приведёт к усложнению кода, который будет очень тяжело поддерживать. Вместо этого нам хотелось бы разбить код на модульные части, с которыми будет проще работать, и которые сделают разработку более гибкой.
Начнём с того, что возьмём существующий код и перенесём его в новый компонент.
Вот как в файле main.js регистрируется компонент:
app.component('product', {})
Первый аргумент — это выбранное нами имя компонента. Второй — это объект с опциями, похожий на тот, который мы использовали при создании экземпляра Vue на прошлых занятиях.
В экземпляре Vue мы использовали идентификатор #app для организации его привязки к элементу
DOM. В случае с компонентом используется свойство template, которое определяет HTML-код
компонента.
Опишем шаблон компонента в объекте с опциями:
app.component('product', {
template: `
<div class="product">
… // Здесь будет весь HTML-код, который раньше был в элементе с классом product
</div>
`
})
Во Vue есть несколько способов создания шаблонов. Сейчас мы пользуемся шаблонным литералом, содержимое которого заключено в обратные кавычки.
Теперь, когда в шаблоне находится HTML-код, который раньше был в файле index.html, мы можем добавить в компонент данные, методы, вычисляемые свойства, которые раньше были в корневом экземпляре Vue:
app.component('product', {
template: `
<div class="product">
…
</div>
`,
data() {
return {
// тут будут данные
}
},
methods: {
// тут будут методы
},
computed: {
// тут будут вычисляемые свойства
}
})
Как видите, структура этого компонента практически полностью совпадает со структурой экземпляра Vue, с которым мы работали раньше.
Теперь, когда мы переместили код, связанный с товаром, в собственный компонент product, код
описания корневого экземпляра Vue будет выглядеть так:
const app = Vue.createApp({});
app.component('product', { ... код продукта переехал сюда ... });
app.mount('#app');
Сейчас нам осталось лишь разместить компонент product в коде файла index.html. Это будет
выглядеть так:
<div id="app">
<product></product>
</div>
Если теперь перезагрузить страницу приложения — она примет прежний вид.
Страница приложения
Если теперь заглянуть в инструменты разработчика Vue, там можно заметить наличие сущности Root
и компонента Product.
Анализ приложения с помощью инструментов разработчика Vue
А теперь, просто чтобы продемонстрировать возможности многократного использования компонентов, давайте
добавим в код index.html ещё пару компонентов product. Собственно говоря, именно так
организовано многократное использование компонентов. Код index.html будет выглядеть так:
<div id="app">
<product></product>
<product></product>
<product></product>
</div>
А на странице будет выведено три копии карточки товара.
Обратите внимание на то, что в дальнейшем мы будем работать с одним компонентом product,
поэтому код index.html будет выглядеть так:
<div id="app">
<product></product>
</div>
В приложениях часто нужно, чтобы компоненты принимали бы данные, входные параметры, от родительских
сущностей. В данном случае родителем компонента product является сам корневой экземпляр Vue.
Пусть в корневом экземпляре Vue имеется описание неких данных. Эти данные указывают на то, является ли пользователь обладателем премиум-аккаунта. Код описания экземпляра Vue при этом может выглядеть так:
const app = Vue.createApp({
data() {
return {
premium: true
};
}
});
app.mount('#app');
Давайте решим, что премиум-пользователям полагается бесплатная доставка.
Это означает, что нам нужно, чтобы компонент product выводил бы, в зависимости от того, что
записано в свойство premium корневого экземпляра Vue, разные сведения о стоимости доставки.
Как отправить данные, хранящиеся в свойстве premium корневого экземпляра Vue, дочернему
элементу, которым является компонент product?
Во Vue, для передачи данных от родительских сущностей дочерним, применяется свойство объекта с опциями
props, описываемое у компонентов. Это объект с описанием входных параметров компонента,
значения которых должны быть заданы на основе данных, получаемых от родительской сущности.
Начнём работу с описания того, какие именно входные параметры ожидает получить компонент
product. Для этого добавим в объект с опциями, используемый при его создании, соответствующее
свойство:
app.component('product', {
props: {
premium: {
type: Boolean,
required: true
}
},
// Тут будут описания данных, методов, вычисляемых свойств
})
Обратите внимание на то, что тут используются встроенные возможности Vue по проверке параметров,
передаваемых компоненту. А именно, мы указываем то, что типом входного параметра premium
является Boolean, и то, что этот параметр является обязательным, устанавливая
required в true.
Далее, внесём в шаблон изменение, выводящее переданные объекту параметры. Выведя значение свойства
premium на странице, мы убедимся в правильности работы исследуемого нами механизма.
<p>User is premium: {{ premium }}</p>
Пока всё идёт нормально. Компонент product знает о том, что он будет получать необходимый для
его работы параметр типа Boolean. Мы подготовили место для вывода соответствующих данных.
Но мы пока ещё не передали параметр premium компоненту. Сделать это можно с помощью пользовательского
атрибута, который похож на «трубопровод», ведущий к компоненту, через который ему можно передавать входные
параметры, и, в частности, premium.
Доработаем код в index.html:
<div id="app">
<product :premium="premium"></product>
</div>
Обновим страницу.
Вывод данных, переданных компоненту
Теперь входные параметры передаются компоненту. Поговорим о том, что именно мы только что сделали.
Мы передаём компоненту входной параметр, или «пользовательский атрибут», называемый premium.
Мы привязываем этот пользовательский атрибут, используя конструкцию, представленную двоеточием, к свойству
premium, которое хранится в данных нашего экземпляра Vue.
Теперь корневой экземпляр Vue может передать premium дочернему компоненту
product. Так как атрибут привязан к свойству premium из данных экземпляра Vue,
текущее значение premium будет всегда передаваться компоненту product.
Вышеприведённый рисунок, а именно, надпись User is premium: true, доказывает то, что всё
сделано правильно.
Теперь мы убедились в том, что изучаемый нами механизм передачи данных работает так, как ожидается. Если
заглянуть в инструменты разработчика Vue, то окажется, что у компонента 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. Теперь элемент
<p>Shipping: {{ shipping }}</p>, который присутствовал в коде, с которого мы
сегодня начали работу, сможет вывести сведения о стоимости доставки.
Премиум-пользователь получает бесплатную доставку
Текст Shipping: Free появляется на странице из-за того, что компоненту передан входной
параметр premium, установленный в значение true.
Замечательно! Теперь мы научились передавать данные от родительских сущностей дочерним и смогли воспользоваться этими данными в компоненте для управления стоимостью доставки товаров.
Кстати, стоит отметить, что в дочерних компонентах не следует изменять их входные параметры.
Создайте новый компонент product-details, который должен использовать входной параметр
details и отвечать за визуализацию той части карточки товара, которая раньше формировалась с
использованием следующего кода:
<ul>
<li v-for="detail in details">{{ detail }}</li>
</ul>
Вот заготовка, которую вы можете использовать для решения этой задачи.
Сегодня состоялось ваше первое знакомство с компонентами Vue. Вот что вы узнали:
data() объекта с опциями.props).Входные параметры передаются компонентам через пользовательские атрибуты.
В скором времени в этой статье появятся еще 2 урока:
Они пока не переведены, но их можно пройти в оригинале, включив автопереводчик в браузере.
Цель: Научиться создавать экземпляр Vue и выводить данные на страницу.
Задача:
<div id="app">.title: "Мой первый Vue-проект" и
description: "Это описание моего проекта".title и description на страницу с использованием синтаксиса
Mustache ({{ }}).Проверка:
title и description.Цель: Научиться привязывать данные к атрибутам HTML-элементов.
Задача:
imageUrl: "https://example.com/image.jpg" и
altText: "Пример изображения".v-bind (или её сокращённую форму :) для привязки
imageUrl к атрибуту src элемента <img> и altText
к атрибуту alt.<a> с привязкой атрибута href к данным
link: "https://example.com".Проверка:
Цель: Научиться управлять отображением элементов на странице с помощью условного рендеринга.
Задача:
inStock: true и onSale: false.v-if для отображения текста "В наличии", если
inStock равно true, и "Нет в наличии", если false.v-show для отображения текста "Распродажа", если
onSale равно true.Проверка:
inStock.onSale.Цель: Научиться выводить списки данных на страницу с помощью директивы v-for.
Задача:
sizes: ['S', 'M', 'L', 'XL'].v-for для отображения списка размеров в виде элементов
<li> внутри <ul>.key для каждого элемента списка, используя индекс элемента.Проверка:
Цель: Научиться обрабатывать события с помощью директивы v-on.
Задача:
cart: 0.v-on:click (или её сокращённую форму @click) для
увеличения значения cart на 1 при каждом нажатии на кнопку.cart на странице.Проверка:
cart увеличивается, и это отображается на
странице.Цель: Научиться создавать и использовать компоненты.
Задача:
product-details, который принимает входной параметр details
(массив строк) и отображает их в виде списка <ul>.details: ['80% cotton', '20% polyester', 'Gender-neutral'].Проверка: