Урок 9 :: Components & Props :: Компоненты и реквизиты (пропсы)
Вот, что мы с Вами сделаем на этом уроке.
Пройдем по этой ссылке:
Components & Props ➳
... Все внимательно послушаем, посмотрим, прочитаем.
Затем сделаем конспект (мой см ниже).
Конспект
Компоненты и реквизиты (Components & Props)В этом уроке мы рассмотрим концепцию компонентов и реквизитов.
Чтобы начать этот урок, вы можете либо получить программой git коммандой checkout исходный код в ветке
L9-start
репозитория, либо перейти в CodePen, чтобы взять файлы там.
Примечание переводчика
Чтобы перейти на ветку с файлами начала урока 9 выполните команду:
git checkout L9-start
Наша Цель
Выполнить рефакторинг нашего тестового приложения, чтобы использовать компонент продукта (
product
). И этот компонент пусть использует реквизит (prop
).
Строительные блоки приложения Vue
В современных интерфейсных JavaScript-фреймворках компоненты являются строительными блоками приложения, и это, безусловно, относится и к Vue. Вы можете представить себе компоненты, немного похожие на блоки Лего (Legos), которые вы можете подключать друг к другу в иерархии генеалогического дерева компонентов.
Любая типичная веб-страница может состоять из нескольких компонентов. Обычное дело для комонентов быть "родителями", когда у них есть дочерние компоненты, вложенные в них.
Создание нашего первого компонента
Давайте зайдем в наше приложение и создадим наш первый компонент. Поскольку наше приложение в конечном итоге будет иметь несколько компонентов, мы создадим папку компонентов components, внутри которой создадим наш первый компонент, называемый ProductDisplay.js.
Синтаксис для создания компонента выглядит следующим образом:
📄 components/ProductDisplay.js
app.component('product-display', {})
Первый аргумент - это имя компонента, в данном случае
'product-display'
, а второй аргумент - объект для настройки нашего компонента (аналогично объекту options
, используемому для настройки нашего корневого приложения Vue).
Шаблон
Поскольку нам нужно, чтобы наш компонент имел структуру, мы добавим свойство шаблона (template) и вставим весь HTML-код на основе продукта, который в настоящее время находится в index.html сюда, в литерал шаблона (template literal).
📄 components/ProductDisplay.js
app.component('product-display', { template: /*html*/ `<div class="product-display"> <div class="product-container"> <div class="product-image"> <img v-bind:src="image"> </div> <div class="product-info"> <h1>{{ title }}</h1> <p v-if="inStock">In Stock</p> <p v-else>Out of Stock</p> <div v-for="(variant, index) in variants" :key="variant.id" @mouseover="updateVariant(index)" class="color-circle" :style="{ backgroundColor: variant.color }"> </div> <button class="button" :class="{ disabledButton: !inStock }" :disabled="!inStock" v-on:click="addToCart"> Add to Cart </button> </div> </div> </div>` })
Обратите внимание, что мы не изменили ничего из этого кода, мы просто перемещаем его в компонент отображения продукта
product-display
, чтобы он был там инкапсулирован. Если вам интересно, что там делает /*html*/
, ответ простой. Эта строка активирует расширение VS-кода
es6-string-html
, которое дает нам подсветку синтаксиса, даже если мы находимся в этом литерале шаблона.
Данные и методы
Теперь, когда мы дали этому компоненту его шаблон или его структуру, нам нужно предоставить ему его данные и методы, которые все еще живут в main.js. Так что мы вставим их прямо сейчас:
📄 components/ProductDisplay.js
app.component('product-display', { template: /*html*/ `<div class="product-display"> ... </div>`, data() { return { product: 'Socks', brand: 'Vue Mastery', selectedVariant: 0, details: ['50% cotton', '30% wool', '20% polyester'], variants: [ { id: 2234, color: 'green', image: './assets/images/socks_green.jpg', quantity: 50 }, { id: 2235, color: 'blue', image: './assets/images/socks_blue.jpg', quantity: 0 }, ] } }, methods: { addToCart() { this.cart += 1 }, updateVariant(index) { this.selectedVariant = index } }, computed: { title() { return this.brand + ' ' + this.product }, image() { return this.variants[this.selectedVariant].image }, inStock() { return this.variants[this.selectedVariant].quantity } } })
Мы обязательно удалим корзину (
cart
) из данных (data
) здесь, потому что нам не нужно, чтобы у каждого товара была своя корзина.
Очистка main.js
Теперь, когда мы инкапсулировали весь этот код для конкретного продукта в наш компонент отображения продукта
product-display
, мы можем очистить наш main.js файл.
📄 main.js
const app = Vue.createApp({ data() { return { cart: 0, } }, methods: {} })
Мы оставили опции корзина (
cart
) и методы (methods
), потому что позже мы здесь сделаем новый метод.
Импорт компонента
Для того, чтобы использовать компонент
product-display
, нам необходимо импортировать его в index.html.
📄 index.html
<!-- Import Components --> <script src="./components/ProductDisplay.js"></script>
Теперь, когда компонент импортирован, мы можем использовать его в нашем шаблоне.
📄 index.html
<div id="app"> <div class="nav-bar"></div> <div class="cart">Cart({{ cart }})</div> <product-display></product-display> </div>
Если мы проверим это в браузере, мы увидим, что все по-прежнему отображается так же, как и раньше, но теперь, когда мы все переставили, кнопка "Добавить в корзину" ("Add to Cart") больше не увеличивает корзину. Мы исправим это на следующем уроке.
На данный момент, чтобы показать вам, насколько полезными могут быть эти повторно используемые блоки кода, я собираюсь добавить еще два компонента отображения продукта (
product-display
).
📄 index.html
<div id="app"> <div class="nav-bar"></div> <div class="cart">Cart({{ cart }})</div> <product-display></product-display> <product-display></product-display> <product-display></product-display> </div>
Когда мы обновим браузер, мы увидим, что все они отображаются. Каждый из них функционирует независимо.
Пропсы
Теперь, когда мы начинаем изучать, как инкапсулировать повторно используемый код в эти компоненты, что происходит, когда нашему компоненту нужно что-то, что находится вне его самого? Например, что, если у родителя, так сказать, были какие-то данные о сообщении (
message
), и ребенок нуждался в них? Поскольку компонент имеет свою собственную изолированную область действия, он не может выйти за пределы самого себя, чтобы захватить что-то, что находится за пределами его области действия.
The answer here is props. These are custom attributes for passing data into a component. They function kind of like a funnel, into which you can pass the data the component needs.
Ответ здесь - пропсы (props). Это пользовательские атрибуты для передачи данных в компонент. Они функционируют как воронка, в которую вы можете передавать данные, необходимые компоненту.
Давайте добавим возможность для нашего компонента отображения продукта
product-display
использовать проп (реквизит, prop).
Внедрение в наш компонент проп (реквизит, prop).
Давайте дадим нашему рутовому приложению Vue, расположенному в main.js, новое свойство данных, которое указывает, был ли пользователь пользователем премиум-класса или нет (
premium)
.
📄 main.js
const app = Vue.createApp({ data() { return { cart: 0, premium: true } } })
Если пользователь премиум-класса (
premium
), его доставка будет бесплатной. Поэтому нашему компоненту отображения продукта (product-display
) необходим доступ к этим данным. Другими словами, ему нужен пользовательский атрибут (воронка), в который мы можем передавать эти данные. Давайте добавим это сейчас, и это мы сделаем, предоставив компоненту опцию пропсов (props
) и добавив к нему проп (prop
) под названием премиум (premium
).
📄 components/ProductDisplay.js
app.component('product-display', { props: { premium: { type: Boolean, required: true } }, ... }
Обратите внимание, что свойство пропсов Vue имеет встроенную валидацию, поэтому мы можем указать такие вещи, как тип реквизита (
type
), обязателен ли он (required
) и т.д.
📄 index.html
<div id="app"> <div class="nav-bar"></div> <div class="cart">Cart({{ cart }})</div> <product-display :premium="premium"></product-display> </div>
Обратите внимание, как мы используем сокращение для
v-bind
, чтобы мы могли реактивно получать новое значение premium
, если оно обновляется (от true
до false
).
Используя проп
Теперь, когда наш компонент отображения продукта product-display имеет премиальный проп premium, мы можем использовать его внутри компонента. Помните, мы хотим использовать тот факт, является ли пользователь премиум-классом или нет, чтобы определить, сколько ему нужно заплатить за доставку.
В шаблоне компонента мы добавим:
📄 components/ProductDisplay.js
template: /*html*/ `<div class="product-display"> ... <p>Shipping: {{ shipping }}</p> ... </div>`,
Здесь
shipping
(доставка) это имя нового вычисляемого свойства компонента отображения продукта (product-display
), которое выглядит следующим образом:
📄 components/ProductDisplay.js
computed: { ... shipping() { if (this.premium) { return 'Free' } return 2.99 } }
Вычисляемое свойство проверяет, является ли премиум проп (
premium
) истинным (true
), и если да, возвращает 'Free'
("Бесплатно"). В противном случае он возвращает 2.99
Задача кодирования
Мы подошли к концу этого урока, теперь будем решать новые задачи кодирования.
Вот, что надо сделать.
Создайте новый компонент с именем
'product-details'
, который получал бы details
через проп details
.
Вы можете найти код решения, загрузив L9-end ветвь репозитория или просмотрев решение в Codepen.
Чтобы перейти на ветку с файлами окончания урока 9 выполните команду:
git checkout L9-end
Код файла index.html данного урока
<div id="app"> <div class="nav-bar"></div> <div class="cart">Cart({{ cart }})</div> <product-display :premium="premium"></product-display> </div> <!-- Import App --> <script src="./main.js"></script> <!-- Import Components --> <script src="./components/ProductDisplay.js"></script> <!-- solution --> <script src="./components/ProductDetails.js"></script> <!-- solution --> <!-- Mount App --> <script> const mountedApp = app.mount('#app') </script>
Код файла main.js данного урока
const app = Vue.createApp({ data() { return { cart: 0, premium: true } }, methods: {} })
Код файла ProductDisplay.js данного урока
См здесь: Код файла ProductDisplay.js данного урокаКод файла ProductDetails.js данного урока
См здесь: Код файла ProductDetails.js данного урокаРезультат выполнения данного урока
Cart({{ cart }})
Примечание переводчика
1. Я слегка меняю контент оригинальных файлов курса для тренировки, проверки русификации и т.д.
2. Код для
HTML
я размещаю в этом файле между тегами body
.
3. Обратите внимание, чтобы в консоле браузера не было ошибок.
Теперь Нажмите здесь для перехода к следующему уроку ➳
... или ...