Админка магазина на vue.js. Урок 5. Разбиваем приложение на компоненты
Приложение vue строится на компонентах. Сейчас у нас только один компонент ProductItem, а вся логика содержится в корневом экземпляре vue. Это нехорошо, нам уже сейчас непросто в нем ориентироваться, а при наращивании функционала будет еще сложнее.
В этом уроке мы разобьем приложение на несколько компонентов поменьше. Также добавим две новые страницы и узнаем, как переключаться между ними.
Начнем с самого простого.
Выделяем шапку и подвал в компоненты.
В app.js создадим 2 новых компонента по аналогии с ProductItem. Они очень простые и содержат только верстку.
// Компонент шапки
Vue.component('Header', {
template: `
Webdevkin
Каталог
`
});
// Компонент подвала
Vue.component('Footer', {
template: `
`
});
Мы просто перенесли верстку шапки и подвала из index.html в поле template соответствующих компонентов. Теперь подключим их в index.html
Вместо
Webdevkin
Каталог
напишем
То же самое с подвалом. Вместо
пишем
Все, оба блока вынесены. Переходим к следующему.
Компонент Products
Сейчас весь код работы с фильтрами, вычисляемыми полями и методами написан в new Vue. Давайте создадим новый экземпляр vue вот так
// Экземпляр vue
var app = new Vue({
el: '#app'
});
Это все, остальная логика уйдет в новый компонент Products. Найдем старое объявление vue
Здесь менять много не придется. Посмотрим на начало кода
// Экземпляр vue (старый)
var app = new Vue({
el: '#app',
data: {
// и так далее...
Заменим строку
var app = new Vue({
на
Vue.component('Products', {
Затем удалим el: '#app' - в компоненте оно не нужно. И сделаем поле data не объектом, а функцией, которая возвращает объект - это требование vue.js при создании компонентов.
Получится вот так
data: function() {
return {
products: [],
categories: [],
brands: [],
sortRules: [
{ key :'id:asc', title: 'По порядку' },
{ key :'rating:desc', title: 'По рейтингу' },
{ key :'price:asc', title: 'По цене, сначала дешевые' },
{ key :'price:desc', title: 'По цене, сначала дорогие' }
],
inputSearch: '',
selectCategory: 0,
selectBrand: 0,
minPrice: 0,
maxPrice: 0,
selectSort: 'id:asc'
}
}
Все, с логикой компонента Products закончили. А точнее, мы ее и не меняли, а целиком взяли ранее написанную. Осталось не забыть скопировать верстку в поле template нового шаблона
template: `
Список товаров
Id
Название
Бренд
Цена
Рейтинг
`
И удалить эту верстку из index.html. То есть вместо большой портянки html у нас будет краткое подключение компонента
И вся верстка index.html будет выглядеть гораздо лаконичнее
Прежде чем пойти дальше, поясню насчет компонента Products. Вы заметили, что он получился сложным и громоздким. Было бы неплохо разделить его как минимум на фильтры и таблицу товаров. Я совершенно согласен и мы это сделаем, но чуть позже, в следующих уроках.
Мы можем разделить Products и сейчас. Но несмотря на небольшой размер приложения многие наши данные взаимосвязаны. Будет много мороки с перекидыванием продуктов, брендов, категорий и фильтров из дочерних компонентов в родительские и наоборот. В будущем мы от этого не избавимся, но сможем сделать проще, когда будем работать с однофайловыми компонентами .vue и разберемся с vuex. Поэтому пока оставим громоздкий Products в таком виде и запишем в планы его отрефакторить. Уже в следующем уроке мы сделаем первые шаги по улучшению.
Переходим к следующей теме.
Добавляем новые вкладки-страницы
Давайте сделаем отдельные страницы для категорий и брендов.
Мы уже умеем создавать компоненты и новые страницы сразу сделаем по уму. Создадим компонент на каждую вкладку-страницу
// Компонент вкладка категории
Vue.component('Categories', {
template: `
Категории
`
});
// Компонент вкладка бренды
Vue.component('Brands', {
template: `
Бренды
`
});
Простейший шаблон выводит заголовок страницы, заполнять их будем позже. А сейчас нас больше интересует, как переключаться между этими страницами.
Для начала заведем список всех страниц приложения. В нашем случае это будут вкладки или табы. Добавим в создании экземпляра vue поле data
// Экземпляр vue
var app = new Vue({
el: '#app',
data: {
currentTab: 'Products',
tabs: [{
component: 'Products',
title: 'Товары'
}, {
component: 'Categories',
title: 'Категории'
}, {
component: 'Brands',
title: 'Бренды'
}]
}
});
Массив data.tabs содержит компонент vue и заголовок вкладки, который нужно вывести в меню. currentTab - текущая вкладка, по умолчанию Products.
Выведем в меню шапки все вкладки. Для этого в index.html в компонент Header передадим свойство tabs.
Теперь перейдем в app.js в сам компонент Header и добавим в props свойство tabs. А вместо кнопки-ссылки
Каталог
выведем список кнопок через полученный массив tabs
Полный код будет такой
// Компонент шапки
Vue.component('Header', {
props: ['tabs'],
template: `
Webdevkin
`
});
Если мы обновим страницу, то увидим, что в меню появились три кнопки. Но при клике на них ничего не происходит. Нужно дописать код, который при клике устанавливает нужное значение свойства currentTab и переключает компоненты Products, Categories и Brands.
Основная хитрость здесь в том, что currentTab находится в корневом компоненте, а менять его нужно из дочернего Header. То есть передать данные "наверх". Vue легко позволяет это сделать с помощью $emit. Добавим в Header button событие @click
Новой строкой с @click и $emit мы говорим: при клике на кнопку отправь родительскому компоненту кастомное событие change-tab и данные tab. change-tab мы придумали сами, а tab - это вкладка, которую мы выбрали при клике.
Теперь в index.html, где мы выводили компонент Header, нужно поймать это событие и что-то сделать с данными из tab. Кастомные события обрабатываются точно так же, как и встроенные click, input и прочие. Изменим код в Header добавлением одной строки
Здесь мы говорим: когда сработает событие change-tab, нужно запустить метод changeTab. Точно так же, как бы сделали это при клике.
Метод changeTab просто установит поле currentTab в новое значение. Вот так
methods: {
changeTab: function(tab) {
this.currentTab = tab.component;
}
}
Конечно же, это метод не Header, а корневого экземпляра, там, где и объявлено свойство currentTab.
Остался последний штрих - показывать нужную страницу в зависимости от currentTab. Идем в index.html и ищем строку
Здесь мы явно указываем компонент Products. Давайте поправим код
Здесь мы говорим, что компонент для вывода заранее неизвестен и его нужно брать из свойства currentTab. Обратите внимание на is с двоеточием
:is="currentTab"
Header и Footer мы подключаем через просто is, потому что там названия компонентов известны. А здесь нужно брать не строку, а именно значение из currentTab, поэтому добавляем : для связывания.
Вот теперь все, можно переключать вкладки и наблюдать разные страницы.
Напоследок маленькая хитрость. Представим ситуацию, мы активно работаем с товарами, набили кучу фильтров, переключились соседнюю вкладку добавить новый бренд и вернулись обратно. При этом компонент Products отрендерится заново и фильтры сбросятся. Если хотите сохранять состояние фильтров, да и вообще любых данных компонента, то оберните нужный компонент тегом keep-alive
Вот теперь состояние вкладок не будет теряться при переключении между ними.
Исходники приложения можно скачать здесь
Что будет в следующем уроке?
До сих пор мы писали код, который годится для учебного проекта. Но он не годится для серьезной разработки. Почему?
1. Мы разбили приложения на компоненты, но до сих пор держим их в одном файле app.js.
2. У нас нет никакой структуры приложения, только 3 файла: index.html, app.js и style.css.
3. Мы не можем использовать классные фишки vue вроде однофайловых компонентов.
4. Мы никак не упоминали сборку приложения в dev и production-режиме.
Эти недостатки мы исправим в следующем уроке, где будем разбираться с отличным инструментом vue cli
До встречи!
Все уроки админки на vue.js
- Урок 1. Список товаров
- Урок 2. Фильтры и сортировки
- Урок 3. Новое REST API на чистом PHP
- Урок 4. Правим клиентский код под новое REST API и находим багу
- Урок 5. Разбиваем приложение на компоненты
- Урок 6. Инструмент vue-cli и vue-компоненты
- Урок 7. Flux и Vuex - общие вопросы
- Урок 8. Vuex на практике
- Урок 9. Перерабатываем фильтры
- Урок 10. Добавляем и удаляем бренды
- Урок 11. Обрабатываем ошибки на клиенте и сервере
- Урок 12. Редактируем бренды
- Урок 13. Роутинг
- Урок 14. Карточка товара
Истории из жизни айти и обсуждение кода.