
Vue.js + Vuetify: UI компоненты
Vue.js в сочетании с Vuetify представляет собой мощную комбинацию для создания современных, отзывчивых веб-приложений с красивым Material Design интерфейсом. Vuetify предоставляет готовые компоненты, которые значительно ускоряют разработку и обеспечивают консистентность дизайна.
Vuetify — это библиотека UI компонентов для Vue.js, которая следует принципам Material Design от Google. Она включает более 80 компонентов, готовых к использованию, что позволяет разработчикам сосредоточиться на бизнес-логике, а не на создании интерфейса с нуля.
1. Преимущества Vue.js + Vuetify
- Готовые компоненты — более 80 предустановленных компонентов, включая кнопки, формы, навигацию, таблицы и многое другое
- Material Design — современный и интуитивно понятный дизайн, соответствующий стандартам Google
- Отзывчивость — все компоненты автоматически адаптируются под различные размеры экранов
- Темизация — легко настраиваемые цветовые схемы и стили
- TypeScript поддержка — полная поддержка TypeScript для типизированной разработки
- Accessibility — встроенная поддержка доступности (ARIA атрибуты, навигация с клавиатуры)
- Vue 3 совместимость — полная поддержка Composition API и других возможностей Vue 3
2. Установка и настройка
Создание нового проекта Vue.js
Создайте новый проект с помощью Vue CLI:
npm create vue@latest my-vuetify-app
cd my-vuetify-app
npm install
Установка Vuetify
Добавьте Vuetify в ваш проект:
npm install vuetify@next @mdi/font
Настройка Vuetify
Создайте файл src/plugins/vuetify.js
:
import { createVuetify } from 'vuetify'
import 'vuetify/styles'
import '@mdi/font/css/materialdesignicons.css'
export default createVuetify({
theme: {
defaultTheme: 'light'
}
})
Обновите src/main.js
:
import { createApp } from 'vue'
import App from './App.vue'
import vuetify from './plugins/vuetify'
const app = createApp(App)
app.use(vuetify)
app.mount('#app')
3. Основные компоненты Vuetify
Кнопки и действия
Vuetify предоставляет различные типы кнопок:
<template>
<div>
<!-- Обычная кнопка -->
<v-btn color="primary">Обычная кнопка</v-btn>
<!-- Кнопка с иконкой -->
<v-btn prepend-icon="mdi-heart" color="error">
Нравится
</v-btn>
<!-- Кнопка с загрузкой -->
<v-btn :loading="loading" color="success">
Сохранить
</v-btn>
<!-- Группа кнопок -->
<v-btn-group>
<v-btn>Левая</v-btn>
<v-btn>Центр</v-btn>
<v-btn>Правая</v-btn>
</v-btn-group>
</div>
</template>
<script setup>
import { ref } from 'vue'
const loading = ref(false)
</script>
Формы и ввод данных
Создание форм с валидацией:
<template>
<v-form @submit.prevent="submitForm" v-model="valid">
<v-container>
<v-row>
<v-col cols="12" md="6">
<v-text-field
v-model="form.name"
:rules="nameRules"
label="Имя"
required
variant="outlined"
></v-text-field>
</v-col>
<v-col cols="12" md="6">
<v-text-field
v-model="form.email"
:rules="emailRules"
label="Email"
type="email"
required
variant="outlined"
></v-text-field>
</v-col>
<v-col cols="12">
<v-textarea
v-model="form.message"
label="Сообщение"
rows="4"
variant="outlined"
></v-textarea>
</v-col>
<v-col cols="12">
<v-btn
type="submit"
color="primary"
size="large"
:disabled="!valid"
>
Отправить
</v-btn>
</v-col>
</v-row>
</v-container>
</v-form>
</template>
<script setup>
import { ref, reactive } from 'vue'
const valid = ref(false)
const form = reactive({
name: '',
email: '',
message: ''
})
const nameRules = [
v => !!v || 'Имя обязательно',
v => v.length >= 2 || 'Имя должно содержать минимум 2 символа'
]
const emailRules = [
v => !!v || 'Email обязателен',
v => /.+@.+\..+/.test(v) || 'Email должен быть корректным'
]
const submitForm = () => {
if (valid.value) {
console.log('Форма отправлена:', form)
// Здесь логика отправки формы
}
}
</script>
Навигация
Создание навигационного меню:
<template>
<v-app>
<v-app-bar color="primary">
<v-app-bar-title>Моё приложение</v-app-bar-title>
<v-spacer></v-spacer>
<v-btn variant="text" to="/">Главная</v-btn>
<v-btn variant="text" to="/about">О нас</v-btn>
<v-btn variant="text" to="/contact">Контакты</v-btn>
<v-menu>
<template v-slot:activator="{ props }">
<v-btn
icon="mdi-account"
v-bind="props"
></v-btn>
</template>
<v-list>
<v-list-item @click="profile">
<v-list-item-title>Профиль</v-list-item-title>
</v-list-item>
<v-list-item @click="logout">
<v-list-item-title>Выйти</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
</v-app-bar>
<v-main>
<v-container>
<router-view></router-view>
</v-container>
</v-main>
</v-app>
</template>
<script setup>
const profile = () => {
console.log('Переход в профиль')
}
const logout = () => {
console.log('Выход из системы')
}
</script>
Таблицы данных
Работа с таблицами:
<template>
<v-data-table
:headers="headers"
:items="users"
:search="search"
:loading="loading"
class="elevation-1"
>
<template v-slot:top>
<v-toolbar flat>
<v-toolbar-title>Пользователи</v-toolbar-title>
<v-divider class="mx-4" inset vertical></v-divider>
<v-spacer></v-spacer>
<v-text-field
v-model="search"
append-icon="mdi-magnify"
label="Поиск"
single-line
hide-details
></v-text-field>
<v-btn
color="primary"
prepend-icon="mdi-plus"
@click="dialog = true"
>
Добавить
</v-btn>
</v-toolbar>
</template>
<template v-slot:item.actions="{ item }">
<v-icon
size="small"
class="me-2"
@click="editItem(item.raw)"
>
mdi-pencil
</v-icon>
<v-icon
size="small"
color="error"
@click="deleteItem(item.raw)"
>
mdi-delete
</v-icon>
</template>
</v-data-table>
</template>
<script setup>
import { ref } from 'vue'
const search = ref('')
const loading = ref(false)
const dialog = ref(false)
const headers = [
{ title: 'ID', key: 'id' },
{ title: 'Имя', key: 'name' },
{ title: 'Email', key: 'email' },
{ title: 'Роль', key: 'role' },
{ title: 'Действия', key: 'actions', sortable: false }
]
const users = ref([
{ id: 1, name: 'Иван Иванов', email: 'ivan@example.com', role: 'Админ' },
{ id: 2, name: 'Мария Петрова', email: 'maria@example.com', role: 'Пользователь' },
{ id: 3, name: 'Алексей Сидоров', email: 'alex@example.com', role: 'Модератор' }
])
const editItem = (item) => {
console.log('Редактирование:', item)
}
const deleteItem = (item) => {
console.log('Удаление:', item)
}
</script>
4. Адаптивный дизайн
Vuetify автоматически адаптирует компоненты под различные размеры экранов:
<template>
<v-container fluid>
<v-row>
<!-- На мобильных устройствах занимает всю ширину -->
<!-- На планшетах и десктопах - половину -->
<v-col cols="12" md="6">
<v-card>
<v-card-title>Карточка 1</v-card-title>
<v-card-text>
Содержимое карточки
</v-card-text>
</v-card>
</v-col>
<v-col cols="12" md="6">
<v-card>
<v-card-title>Карточка 2</v-card-title>
<v-card-text>
Содержимое карточки
</v-card-text>
</v-card>
</v-col>
</v-row>
<!-- Скрытие/показ элементов в зависимости от размера экрана -->
<v-row>
<v-col cols="12">
<v-btn
class="d-md-none"
color="primary"
>
Мобильная кнопка
</v-btn>
<v-btn
class="d-none d-md-block"
color="secondary"
>
Десктопная кнопка
</v-btn>
</v-col>
</v-row>
</v-container>
</template>
5. Темизация и кастомизация
Настройка цветовой схемы
// src/plugins/vuetify.js
import { createVuetify } from 'vuetify'
import 'vuetify/styles'
export default createVuetify({
theme: {
themes: {
light: {
colors: {
primary: '#1867C0',
secondary: '#5CBBF6',
accent: '#82B1FF',
error: '#FF5252',
info: '#2196F3',
success: '#4CAF50',
warning: '#FFC107'
}
},
dark: {
colors: {
primary: '#2196F3',
secondary: '#424242',
accent: '#FF4081'
}
}
}
}
})
Переключение темы
<template>
<v-app>
<v-app-bar>
<v-app-bar-title>Приложение</v-app-bar-title>
<v-spacer></v-spacer>
<v-btn
icon
@click="toggleTheme"
>
<v-icon>
{{ theme.global.current.value.dark ? 'mdi-weather-sunny' : 'mdi-weather-night' }}
</v-icon>
</v-btn>
</v-app-bar>
<v-main>
<v-container>
<v-card>
<v-card-title>Контент</v-card-title>
<v-card-text>
Тема автоматически применяется ко всем компонентам
</v-card-text>
</v-card>
</v-container>
</v-main>
</v-app>
</template>
<script setup>
import { useTheme } from 'vuetify'
const theme = useTheme()
const toggleTheme = () => {
theme.global.name.value = theme.global.current.value.dark ? 'light' : 'dark'
}
</script>
6. Интеграция с Vue Router
Настройка маршрутизации:
// src/router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import Home from '../views/Home.vue'
import About from '../views/About.vue'
import Contact from '../views/Contact.vue'
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/about',
name: 'About',
component: About
},
{
path: '/contact',
name: 'Contact',
component: Contact
}
]
const router = createRouter({
history: createWebHistory(),
routes
})
export default router
7. Лучшие практики
Производительность
- Используйте
v-show
вместоv-if
для часто переключаемых элементов - Применяйте
v-memo
для оптимизации рендеринга списков - Используйте ленивую загрузку компонентов
<template>
<div>
<!-- Ленивая загрузка тяжелого компонента -->
<Suspense>
<template #default>
<LazyComponent />
</template>
<template #fallback>
<v-progress-circular indeterminate></v-progress-circular>
</template>
</Suspense>
</div>
</template>
<script setup>
import { defineAsyncComponent } from 'vue'
const LazyComponent = defineAsyncComponent(() => import('./LazyComponent.vue'))
</script>
Доступность
- Всегда добавляйте
aria-label
для иконок без текста - Используйте семантические HTML теги
- Обеспечивайте навигацию с клавиатуры
<template>
<v-btn
icon="mdi-delete"
aria-label="Удалить элемент"
@click="deleteItem"
></v-btn>
</template>
8. Полезные ссылки
Заключение
Vue.js + Vuetify — это отличная комбинация для создания современных веб-приложений. Vuetify предоставляет богатый набор готовых компонентов, которые следуют принципам Material Design и автоматически адаптируются под различные устройства.
Основные преимущества этого стека:
- Быстрая разработка благодаря готовым компонентам
- Красивый и современный дизайн
- Отличная производительность
- Простота изучения и использования
- Большое сообщество и документация
Начните с простых компонентов и постепенно осваивайте более сложные возможности. Vuetify предоставляет все необходимые инструменты для создания профессиональных веб-приложений.