Vue.js + Vuetify: UI компоненты
2025-08-11

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 предоставляет все необходимые инструменты для создания профессиональных веб-приложений.