Vuetify  —  создаем свое простое приложение

от автора

Хоть UI библиотек или фреймворков для Vue.js не так уж и много, но Vuetify как раз здесь больше всего выделяется и по функционалу просто впереди других. Сегодня в этой небольшой статье я расскажу об данном UI фреймворке, его особенности, его структура и вообще к чему его можно применять. И честно в интернете не так уж и много русскоязычных материалов под данной теме, поэтому я решил что-то написать по Vuetify.

P.S. Вот сыллка на GitHub с кодом приложения.

Введение/Установка

Окей, представим что вы решили все таки начать пользоваться Vuetify и написать какое-то своё первое приложение используя его. И начинается все конечно же с установки, для начала давайте создадим новое Vue CLI приложение с помощью npm:

vue create vuetify-app

Важно, надо чтобы приложение имело версию Vue 2.0, поскольку на время написания этой статьи в Vuetify поддержка идёт только для Vue 2.0 приложений. После того как нового приложение было создано  —  следующим шагом будет установить UI фреймворк как плагин:

vue add vuetify

Нас попросит в дальнейшем выбрать какой-то пресет, и поскольку мы не собираемся пробовать версию V3 (которая нестабильна), то мы выбираем пресет “Default”:

Когда процесс установки Vuetify завершиться, то можно будет запустить локальный сервер приложения:

npm run serve

И открыв https://localhost:8080 мы увидим по-умолчанию созданную страницу от Vuetify с полезными сыллками:

Как мы видим то все работает и самое время начать уже писать наше приложение.

Так о чем же будет наше приложение?

Хороший вопрос, ведь мы создали уже Vue CLI приложение и установили Vuetify, но я так и не сказал что же будет делать именно наше приложение. Мой любимый пример, это мобильное приложение по доставке еды  —  поэтому давайте это будет тема нашего приложения.

Но перед этим давайте также установим Vue Router:

vue add router

Как обычно при добавлении нового плагина к приложению, терминал может сказать что есть изменения которые не были добавлены в commit, мы здесь пишем “y” (т.е. yes) и двигаемся дальше:

Дальше при установке нас спросит хотим ли использовать режим истории в маршутизации, и здесь мы также тыкаем “y”:

Вот теперь Vue Router установлен, и самое время убрать код примеров в src/router/index.js чтобы когда мы сейчас запустили снова локальный сервер  —  не было ошибок:

// Файл src/router/index.js import Vue from 'vue' import VueRouter from 'vue-router' Vue.use(VueRouter) const routes = [    ] const router = new VueRouter({   mode: 'history',   base: process.env.BASE_URL,   routes }) export default router

Файл маршутизации теперь настроен и не содержит в себе каких-то несуществующих маршрутов, ну а теперь нужно также почистить файл App.vue:

<!-- App.vue --> <template>   <div id="app">     <!-- Сейчас мы будем здесь писать свой код-->   </div> </template> <script> export default {   name: 'App' } </script>

Хорошо, теперь мы настроили наши два файла и при запуске не будет никаких ошибок  —  а самое лучшее это то  —  что мы можем начинать разрабатывать наше приложение.

Layout

В Vuetify уже есть своя придуманная гибкая система построения сетки в приложении. И давайте сейчас с ней ознакомимся. Для начала нам нужно будет воспользоваться двумя компонентами, это v-app и v-main. Открываем App.vue и добавляем их:

<template>   <v-app>         <v-main>    </v-main>        </v-app> </template> <script> export default {   name: 'App' } </script>

С компонента v-app и начинается наше приложение, этот компонент является псевдонимом тега <div> с id=”app”, а в компоненте v-main мы уже начинаем размещать контент самого приложения. Следующим шагом будет добавить хедер с помощью компонента v-app-bar:

<template>   <v-app>          <v-app-bar       app     >          </v-app-bar>          <v-main>     </v-main>        </v-app> </template> <!-- <script> -->

Если мы перейдем на локальный сервер  —  то увидим следующее:

А на странице пустовато
А на странице пустовато

На странице нету ничего кроме нашего хедера. Объясню также для чего я добавил атрибут app в v-app-bar. Когда Vuetify видит что в компоненте v-app-bar есть атрибут app, то он понимает что это часть layout’a приложения. Многие также могут спросить почему я добавил хедер перед v-main, ответ простой: в v-main будет уже располагаться компонент router-view который будет показывать содержимое маршрутов, а поскольку наш хедер это не сам контент приложения  —  то я и вынес его перед компонентом v-main.

Мне не очень нравится цвет хедера, поэтому давайте его поменяем добавив атрибут color:

<template>   <v-app>          <v-app-bar       app       color="red"     >       <!-- Сейчас добавим сюда title -->     </v-app-bar>          <v-main>       <!-- Контент -->     </v-main>        </v-app> </template>

Окей, и что если мы перейдем на локальный сервер? То вот что мы с вами увидим:

С красным цветом хедер выглядит уже намного лучше
С красным цветом хедер выглядит уже намного лучше

Круто, а теперь мы можем начинать добавлять уже какой-то контент? Да, но перед этим добавим также заголовок в хедер и компонент просмотра маршутов внутри v-main:

<template>   <v-app>          <v-app-bar       app       color="red"     >       <v-app-bar-title>         <h3 class="text-h4 white--text">iFood</h3>       </v-app-bar-title>     </v-app-bar>          <v-main>       <router-view></router-view>     </v-main>        </v-app> </template>

Заголовок мы добавили с помощью компонента v-app-bar-title, и уже внутри прописали тег h4 с классом от Vuetify которые помогают стилизировать текст. Давайте посмотрим как выглядит страница на локальном сервере:

Вот и наш заголовок появился
Вот и наш заголовок появился

Супер, заголовок есть. Хорошо, теперь самое время начать создавать компоненты в которых и будет контент приложения.

Главная страница

Давайте создадим компонент src/components/List.vue в которым и будет выводиться весь список заведений с помощью JSON массива. Для начала внутри этого компонента создадим переменную items_list, которая и будет хранить в себе список всех заведений:

<!-- src/components/List.vue --> <template>   <div>     <!-- Пока здесь будет пустой div -->   </div> </template> <script> export default {   name: 'List',   data: () => ({     items_list: [       {         id: 0,         title: 'Заведение 1',         description: 'Это наверное самое крутое заведение которое вы можете знать',         img: '' // Здесь добавляем любое изображение       },       {         id: 1,         title: 'Заведение 2',         description: 'Lorem ipsum',         img: '' // Здесь добавляем любое изображение       }       // Дальше можете добавить другие заведения в список     ]   }) } </script>

Окей, список у нас есть, а с помощью каких компонентов от Vuetify будем его выводить? Сейчас воспользуемся компонентом v-card, который позволяет удобно в карточках выводить подобную информацию. Но при этом мы также воспользуемся Bootstrap Vue чтобы элементы красиво располагались на странице.

<!-- src/components/List.vue --> <template>   <v-container> <!-- Создаем контейнер внутри которого и будут элементы компонента -->          <v-row class="list__cafes-title"> <!-- В это row выводим заголовок компонента -->       <v-col>         <h2 class="text-center text-h3 py-3">List of cafes</h2>       </v-col>     </v-row>          <v-row class="list__cafes-content">       <v-col md="4" v-for="item in items_list" :key="item.id">         <v-card>                      <v-img             height="250"             :src="item.img"           ></v-img> <!-- С помощью v-img добавляем изображение карточки -->                      <v-card-title> <!-- Заголовок заведения -->             <h3 class="text-h4">{{ item.title }}</h3>           </v-card-title>                      <v-card-text> <!-- Описание заведения -->             <p class="text-body-1">{{ item.description }}</p>           </v-card-text>                  </v-card>       </v-col>     </v-row>      </v-container> </template>

Многие могут заметить что в row’e где мы уже выводим сами карточки заведения с помощью v-for в компоненте v-col я добавляю атрибут md=”4«. Те кто учили Bootstrap уже должны догадаться для чего нужен этот атрибут и как именно он задает размеры колонок. С помощью этого атрибута я указал чтобы каждая колонка имела размеры 4/12 и благодаря этому на странице карточки будут выводиться по 3 штуки в один ряд. Окей, компонент вывода заведений готов, надо теперь добавить его маршрут в файл src/router/index.js:

import Vue from 'vue' import VueRouter from 'vue-router' import List from '../components/List.vue' Vue.use(VueRouter) const routes = [   {     path: '/',     name: 'List',     component: List   } ] const router = new VueRouter({   mode: 'history',   base: process.env.BASE_URL,   routes }) export default router

А теперь давайте перейдем на локальный сервер и посмотрим на результат вывода:

На декстоп и на мобайл выглядит нормально, можем двигаться дальше
На декстоп и на мобайл выглядит нормально, можем двигаться дальше

Супер​ У нас есть вывод заведений на главной странице. А теперь я думаю самое время добавить отдельную страницу заведения, которая будет открываться при клике на заголовок карточки. Поскольку у нас нету базы данных с которой мы могли бы брать определенное заведение через API  —  то сама страница будет статичной.

Страница просмотра заведения

Для начала создаём компонент src/components/CafeView.vue и первое что в нем будет выводиться это изображение с заведением, его заголовок и описание. Для этой базовой информации мы создадим отдельный компонент с названием CafeInfo.vue, но перед этим заполним уже чем-то компонент CafeView.vue:

<!-- src/components/CafeView.vue --> <template>   <v-container>          <CafeInfo></CafeInfo> <!-- Этот компонент мы сейчас создадим -->        </v-container> </template>  <script> import CafeInfo from './Cafe/CafeInfo.vue' export default {   name: 'CafeView',   components: {     CafeInfo   } } </script>

Хорошо, а теперь создаём компонент CafeInfo.vue:

<!-- src/components/Cafe/CafeInfo.vue --> <template>   <v-row>          <v-col md="6">              <v-card class="h-100">         <v-img           height="350"           :src="main_info.img"           class="d-flex align-end"         >           <!--             Выше можете заметить что с помощью классов d-flex и align-end, мы прописали CSS свойства display: flex; и align-items: flex-end. Это сделает так что название заведения будет выводиться внизу внутри изображения           -->           <h2 class="text-h4 ml-4 mb-4 white--text">{{ main_info.title }}</h2>         </v-img>       </v-card>            </v-col>     <v-col md="6">               <v-card class="h-100">                    <v-card-text>           <p class="font-weight-light">{{ main_info.description }}</p>          </v-card-text>                  </v-card>            </v-col>        </v-row>    </template> <script> export default {   name: 'CafeInfo',   data: () => ({     main_info: {       img: 'https://example.com/image_1.jpg',       title: 'Заголовок заведения',       description: 'А здесь уже описание заведения'     }   }) } </script>  <style> .h-100 {   height: 100%; } </style>

Для удобства я специально поместил данные в data функцию. Теперь когда наш компонент готов  —  самое время сделать маршрут для компонента CafeView.vue в src/routes/index.js:

import Vue from 'vue' import VueRouter from 'vue-router' import List from '../components/List.vue' import CafeView from '../components/CafeView.vue'  Vue.use(VueRouter)  const routes = [   {     path: '/',     name: 'List',     component: List   },   {     path: '/cafe/view',     name: 'CafeView',     component: CafeView   } ] // ...

Окей, а теперь давайте посмотрим как это выглядит вместе на локальном сервере на url http://localhost:8080/cafe/view:

Вид можно было б ещё больше кастомизировать чтобы выглядело намного красивее. Но пусть будет так с стилями Vuetify по-умолчанию. Теперь давайте также пропишем сыллку на переход на эту страницу в List.vue:

<!-- src/components/List.vue --> <template>   <v-container>          <v-row class="list__cafes-title">       <v-col>         <h2 class="text-center text-h3 py-3">List of cafes</h2>       </v-col>     </v-row>          <v-row class="list__cafes-content">       <v-col md="4" v-for="item in items_list" :key="item.id">                  <v-card>                      <v-img             height="250"             :src="item.img"           ></v-img>                      <v-card-title>             <router-link to="/cafe/view">               <h3 class="text-h4">{{ item.title }}</h3>             </router-link>           </v-card-title>                      <v-card-text>             <p class="text-body-1">{{ item.description }}</p>           </v-card-text>                    </v-card>                </v-col>     </v-row>   </v-container> </template>

В массив объектов заведений можно было бы ещё добавить отдельное поле url, которое хранило в себе линки на страницу заведения  —  но поскольку само приложение у нас статичное, то мы сделали именно так. Вывода такой информации на странице заведения — недостаточно. Нужно ещё реализовать компонент вывода меню товаров (еды) заведения. Поэтому для этого мы сейчас создадим отдельный компонент CafeMenu.vue.

Компонент меню заведения

Сначала создайте компонент src/components/Cafe/CafeMenu.vue, и внутри него сначала создаем переменную с массивом внутри которого будут объекты с информацией об товарах:

<!-- src/components/Cafe/CafeMenu.vue --> <template>   <v-row>     <!-- Пока здесь будет пустой row -->   </v-row> </template>  <script> export default {   name: 'CafeMenu',   data: () => ({     menu_items: [       {         id: 0,         name: 'King Burger',         img: 'https://example.com/burger_1.png',         price: 34.99       },       {         id: 1,         name: 'Dark Burger',         img: 'https://example.com/burger_2.png',         price: 39.99       },       // Можете также дальше добавить другие товары      ]   }) } </script>

Рекомендую также добавить другие товары в список. А теперь самое время вывести их в отдельной колонке:

<!-- src/components/Cafe/CafeMenu.vue --> <template>   <v-row>          <v-col md="6">       <v-card>                  <v-img           height="250"           src="https://example.com/menu_img.png"           class="d-flex align-end"         >           <h2 class="ml-4 mb-4 text-h3 white--text">MENU</h2>         </v-img>                </v-card>     </v-col>          <v-col md="6">       <v-card>                  <v-card-text>           <!--             Здесь внутри начинаем выводить товары,             для этого создаем список           -->           <v-list-item-group>                          <v-list-item v-for="item in menu_items" :key="item.id">                              <v-list-item-avatar>                 <!-- Выводим изображение товара -->                 <img :src="item.img" fluid />               </v-list-item-avatar>                              <v-list-item-content>                                  <v-list-item-title>                   <!-- Выводим название товара -->                   <h5 class="text-h5">{{ item.name }}</h5>                 </v-list-item-title>                                  <v-list-item-subtitle>                   <!-- Выводим цену товару -->                   <b>{{ item.price }}</b>                 </v-list-item-subtitle>                                </v-list-item-content>                              <v-list-item-action>                 <!-- Добавляем кнопку для покупки товара -->                 <v-btn icon>                   <v-icon>mdi-plus</v-icon>                 </v-btn>               </v-list-item-action>                            </v-list-item>                        </v-list-item-group>         </v-card-text>                </v-card>          </v-col>        </v-row> </template>

С помощью компонента v-list-item-group мы создали список, внутри которого с помощью v-for в v-list-item вывели список всех товаров. Компонент v-list-item-content используется для того чтобы выводить контент самого элемента списка, в нашем случае мы выводим заголовок (v-list-item-title) и цену (с помощью v-list-item-subtitle). А с помощью v-list-item-action, внутри мы добавили кнопку покупки товара (v-btn), атрибут icon мы добавили чтобы указать что внутри кнопки выводиться только иконка. 

Внутри v-icon мы указали иконку от Material Design. Окей, теперь когда я объяснил что к чему — можем подключить данный компонент:

<!-- src/components/CafeView.vue --> <template>   <v-container>          <CafeInfo></CafeInfo>     <CafeMenu></CafeMenu>        </v-container> </template>  <script> import CafeInfo from './Cafe/CafeInfo.vue' import CafeMenu from './Cafe/CafeMenu.vue' export default {   name: 'CafeView',   components: {     CafeInfo,     CafeMenu   } } </script>

Открываем локальный сервер и смотрим на результат:

Круто, но нашему приложению не хватает менюшки в хедере очень сильно. Поэтому давайте её сейчас добавим.

Создание навигации

Сейчас нам нужно перейти в компонент App.vue и добавить новый компонент v-navigation-drawer:

<template>   <v-app>          <v-navigation-drawer       app       left     ></v-navigation-drawer>          <v-app-bar       app       color="red"     >       <v-app-bar-title>         <h3 class="text-h4 white--text">iFood</h3>       </v-app-bar-title>     </v-app-bar>          <v-main>       <router-view></router-view>     </v-main>        </v-app> </template>  <script> export default {   name: 'App' } </script>

Этот компонент позволяет создавать меню навигации, я специально прописал атрибут left чтобы пустое меню навигации появилось с левой стороны:

Выглядит немного страшновато, но сейчас мы это пофиксим. Добавлю также что v-navigation-drawer является одним из компонентов layout системы Vuetify, и именно он отвечает за меню навигации. Давайте добавим следующий код чтобы это меню срабатывало при клике на иконку в хедере:

<template>   <v-app>          <v-navigation-drawer       app       left       absolute       temporary       v-model="drawer"     ></v-navigation-drawer>          <v-app-bar       app       color="red"     >       <v-app-bar-nav-icon @click="drawer = !drawer">       </v-app-bar-nav-icon>       <v-app-bar-title>         <h3 class="text-h4 white--text">iFood</h3>       </v-app-bar-title>     </v-app-bar>          <v-main>       <router-view></router-view>     </v-main>        </v-app> </template>  <script> export default {   name: 'App',   data: () => ({     drawer: false   }) } </script>

Зачем я добавил атрибуты absolute, temporary и v-model в v-navigation-drawer? Во-первых, absolute задает CSS свойство position: absolute; а это позволит сделать так чтобы эта менюшка и на мобайл хорошо отображалась. Во-вторых, temporary задает z-index для навигации — чтобы другие элементы не залазили на неё. 

И во-третьих, v-model имеет в себе переменную drawer, которая в зависимости от значения true или false будет показывать меню. К примеру, если переменная drawer равняется в текущую момент значению true — то менюшка будет показываться. Если false, то менюшка будет спрятана. Если вы сейчас перейдете на локальный сервер — то увидите в хедере иконку и при клике на неё будет вылазить наше меню:

Вот здесь мы и можем увидеть иконку в хедере
Вот здесь мы и можем увидеть иконку в хедере
А при клике на саму иконку вылазит пустое меню с левой стороны
А при клике на саму иконку вылазит пустое меню с левой стороны

Теперь нужно чем-то заполнить меню, для этого создадим сейчас отдельную переменную с пунктами для меню:

<!-- ./ template --> <script> export default {   name: 'App',   data: () => ({     drawer: false,     menu_items: [       {         id: 0,         name: 'Список заведения',         url: '/'       },       {         id: 1,         name: 'О нас',         url: '#'       },       {         id: 2,         name: 'Контакты',         url: '#'       },       {         id: 3,         name: 'Помощь',         url: '#'       }     ]   }) } </script>

А теперь выведем все это внутри v-navigation-drawer:

<template>   <v-app>          <v-navigation-drawer       app       left       absolute       temporary       v-model="drawer"     >              <v-list-item-group>                  <v-list-item v-for="item in menu_items" :key="item.id">           <v-list-item-title>             <router-link :to="item.url">{{ item.name }}</router-link>           </v-list-item-title>         </v-list-item>                </v-list-item-group>            </v-navigation-drawer>          <v-app-bar       app       color="red"     >       <v-app-bar-nav-icon @click="drawer = !drawer"></v-app-bar-nav-icon>       <v-app-bar-title>         <h3 class="text-h4 white--text">iFood</h3>       </v-app-bar-title>     </v-app-bar>          <v-main>       <router-view></router-view>     </v-main>        </v-app> </template>

Готово, теперь если мы откроем меню то увидим следующее:

И вот у нас есть менюшка которая выглядет не прям красиво, но она работает и позволяет уже переходить на страницы. Окей, и что у нас получается? У нас есть простое приложение с выводом заведений, и просмотром этих заведений. Все это довольно таки примитивно — то мы смогли уже сделать что-то с Vuetify.

Итог

Окей, нам получилось сделать просто приложение с неким дизайном с помощью UI фреймворка для Vue.js — Vuetify. Можно было б и больше рассказать — но это сделало статью ещё более длинной, поэтому если кто-то хочет узнать более подробно по данной теме, то может посмотреть моё видео где мы создаём аналогичное приложение — только здесь уже идёт больше объяснений что и как и куда. Кому интересно может заглянуть.

Я лишь скажу что считаю Vuetify лучшим UI фреймворком для Vue.js, и использование его сильно облегчает разработку нового Vue приложения. Ведь тут уже столько всего поддерживает, и нам не приходится изобретать велосипеды.


ссылка на оригинал статьи https://habr.com/ru/articles/575050/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *