Element Plus — готовый UI для Vue

Element Plus — библиотека готовых UI-компонентов для Vue 3: кнопки, формы, таблицы, диалоги, уведомления и датапикеры. Вместо того чтобы верстать каждый элемент вручную, ты берёшь готовый компонент и настраиваешь его через props.

Уровень: средний 17 разделов ⏱ 2 часа

1. Что такое UI-библиотека

UI-библиотека — набор стилизованных компонентов с единым дизайном и доступностью. Плюсы Element Plus:

Element Plus подходит для админок, дашбордов и внутренних систем. Для лендингов с уникальным дизайном ручная вёрстка всё равно нужнее — база из курса CSS и Sass.

2. Установка и подключение

npm install element-plus
npm install @element-plus/icons-vue
// main.ts — полное подключение (проще всего)
import { createApp } from "vue";
import ElementPlus from "element-plus";
import "element-plus/dist/index.css";
import * as ElementPlusIconsVue from "@element-plus/icons-vue";

const app = createApp(App);
for (const [name, comp] of Object.entries(ElementPlusIconsVue)) {
  app.component(name, comp);
}
app.use(ElementPlus).mount("#app");
// Auto-import: компоненты подхватываются автоматически
npm install -D unplugin-vue-components unplugin-auto-import
// vite.config.ts
import AutoImport from "unplugin-auto-import/vite";
import Components from "unplugin-vue-components/vite";
import { ElementPlusResolver } from "unplugin-vue-components/resolvers";

plugins: [
  AutoImport({ resolvers: [ElementPlusResolver()] }),
  Components({ resolvers: [ElementPlusResolver()] })
]

3. Кнопки: el-button

<el-button>По умолчанию</el-button>
<el-button type="primary">Основная</el-button>
<el-button type="success" round>Скруглённая</el-button>
<el-button type="danger" :disabled="loading">Удалить</el-button>
<el-button type="primary" :icon="Search" circle />

Типы: primary, success, warning, danger, info. Атрибуты: round, circle, plain, text, link, disabled.

4. Формы: el-form и валидация

<script setup lang="ts">
import { reactive } from "vue";
import { FormInstance, FormRules } from "element-plus";

const formRef = ref<FormInstance>();
const form = reactive({ email: "", password: "" });

const rules: FormRules = {
  email: [
    { required: true, message: "Email обязателен", trigger: "blur" },
    { type: "email", message: "Некорректный email", trigger: "blur" }
  ],
  password: [
    { min: 6, message: "Минимум 6 символов", trigger: "blur" }
  ]
};

function submit() {
  formRef.value?.validate((valid) => {
    if (valid) console.log("OK", form);
  });
}
</script>

<template>
  <el-form ref="formRef" :model="form" :rules="rules" label-width="100px">
    <el-form-item label="Email" prop="email">
      <el-input v-model="form.email" placeholder="you@example.com" />
    </el-form-item>
    <el-form-item label="Пароль" prop="password">
      <el-input v-model="form.password" type="password" show-password />
    </el-form-item>
    <el-button type="primary" @click="submit">Войти</el-button>
  </el-form>
</template>
Валидация правил — поле rules с триггерами blur / change. Механика похожа на то, что ты видел в формах Vue 3.

5. Таблицы: el-table

<el-table :data="products" stripe style="width: 100%">
  <el-table-column prop="id" label="ID" width="60" />
  <el-table-column prop="title" label="Название" />
  <el-table-column prop="price" label="Цена" width="120">
    <template #default="{ row }">
      {{ row.price }} ₽
    </template>
  </el-table-column>
  <el-table-column label="Действия" width="140">
    <template #default="{ row }">
      <el-button size="small" @click="onEdit(row)">Изменить</el-button>
    </template>
  </el-table-column>
</el-table>

Полезные атрибуты: stripe (зебра), border, sortable, filterable, пагинация через pagination.

6. Диалоги: el-dialog

<el-button @click="dialogVisible = true">Открыть</el-button>

<el-dialog v-model="dialogVisible" title="Создать товар" width="480px">
  <el-input v-model="form.title" placeholder="Название" />
  <template #footer>
    <el-button @click="dialogVisible = false">Отмена</el-button>
    <el-button type="primary" @click="dialogVisible = false">Сохранить</el-button>
  </template>
</el-dialog>
Управление видимостью — через v-model на visible. Не забудь закрыть диалог вопросом: данные формы теряются при закрытии, если не сохранить заранее.

7. Уведомления: ElMessage и ElNotification

import { ElMessage, ElNotification } from "element-plus";

ElMessage.success("Товар добавлен");
ElMessage.warning("На складе мало товара");
ElMessage.error("Не удалось сохранить");

ElNotification({
  title: "Готово",
  message: "Отчёт сформирован",
  type: "success",
  position: "bottom-right",
  duration: 3000
});
Message — лёгкие «тосты», Notification — более крупные панели. Оба исчезают сами через duration (мс).

8. Темизация через CSS-переменные

Все цвета библиотеки — CSS-переменные вида --el-color-primary. Переопредели их глобально:

// assets/element.css
:root {
  --el-color-primary: #2563eb;
  --el-color-primary-light-3: #4f83ef;
  --el-color-success: #16a34a;
  --el-color-danger: #dc2626;
  --el-border-radius-base: 8px;
}
// main.ts — после стилей библиотеки
import "element-plus/dist/index.css";
import "./assets/element.css";

9. ConfigProvider и локализация

Датапикеры и пагинация по умолчанию по-английски. Подключи русскую локаль:

import ru from "element-plus/es/locale/lang/ru";   // русская локаль

<el-config-provider :locale="ru">
  <App />
</el-config-provider>
У Element Plus есть и более интересная возможность — size и zIndex, заданные один раз на контейнере, действуют на все вложенные компоненты.

10. Меню и навигация: el-menu, el-tabs

Навигация — сердце любой админки. Горизонтальное или вертикальное меню строится через el-menu:

<el-menu mode="vertical" :default-active="$route.path"
          router @select="onSelect">
  <el-menu-item index="/dashboard">Дашборд</el-menu-item>
  <el-sub-menu index="/products">
    <template #title>Товары</template>
    <el-menu-item index="/products/list">Список</el-menu-item>
    <el-menu-item index="/products/categories">Категории</el-menu-item>
  </el-sub-menu>
</el-menu>

Вкладки el-tabs организуют содержимое одной страницы:

<el-tabs v-model="activeTab">
  <el-tab-pane label="Основное" name="main">...</el-tab-pane>
  <el-tab-pane label="SEO" name="seo">...</el-tab-pane>
</el-tabs>
Атрибут router заставляет пункты меню работать как переходы по маршрутам — отдельные клики не нужны.

11. Выбор дат: el-date-picker

Пикер дат умеет диапазоны, недели и кастомные форматы:

<el-date-picker
  v-model="range"
  type="daterange"
  start-placeholder="Начало"
  end-placeholder="Конец"
  value-format="YYYY-MM-DD"
/>
<!-- месяц и год -->
<el-date-picker v-model="month" type="month" />
<!-- быстрые диапазоны -->
<el-date-picker type="datetimerange" :shortcuts="shortcuts" />
const shortcuts = [
  { text: "Сегодня", value: () => [new Date(), new Date()] },
  { text: "7 дней",  value: () => [dayjs().subtract(7, "day"), dayjs()] },
];
Все компоненты Element Plus дружат с dayjs: передавай готовый формат через value-format, чтобы не переводить даты руками.

12. Выпадающие: el-select и el-cascader

Селект с поиском и множественным выбором — база форм:

<el-select v-model="cities" multiple filterable clearable
           placeholder="Выберите города">
  <el-option v-for="c in citiesAdd" :key="c"
             :label="c" :value="c" />
</el-select>

Для иерархии (страна → регион → город) — каскадный выпадающий список:

<el-cascader :options="regions" v-model="path"
             :props="{ value: 'id', label: 'name', children: 'children' }" />
const regions = [
  { id: 1, name: "Россия", children: [
    { id: 11, name: "Москва" },
    { id: 12, name: "Санкт-Петербург" },
  ]},
];
Для больших списков держи опции в отдельном модуле или подгружай lazily через remote-атрибут и событие search.

13. Собираем админку: практика

Теперь всё готово для типовой админки — таблица, форма в диалоге, пагинация:

<el-table :data="rows" v-loading="loading">
  <el-table-column prop="title" label="Название" />
  <el-table-column prop="price" label="Цена" />
  <el-table-column label="Действия" width="160">
    <template #default="{ row }">
      <el-button size="small" @click="edit(row)">Изменить</el-button>
      <el-popconfirm title="Удалить?" @confirm="remove(row)">
        <template #reference>
          <el-button size="small" type="danger">Удалить</el-button>
        </template>
      </el-popconfirm>
    </template>
  </el-table-column>
</el-table>
<el-pagination v-model:current-page="page" :total="total"
               layout="prev, pager, next" />
Соединяй всё с API из курса REST API, а состояние страниц держи в Pinia.
Перед деплоем проверь валидацию форм: пустые поля, длины, типы. Вдвойне важен правильный :model у el-form.

14. Загрузка файлов: el-upload

Компонент el-upload закрывает 90% задач загрузки: картинки, документы, прогресс и валидация на лету:

<el-upload
    action="/api/upload"
    :on-success="onUploaded"
    :limit="1"
    accept="image/*">
  <el-button type="primary">Загрузить изображение</el-button>
</el-upload>
const beforeUpload = (file) => {
  if (file.size > 2 * 1024 * 1024) {
    ElMessage.error("Файл больше 2 МБ");
    return false;
  }
  return true;
};
Ограничивай приём только нужными типами и на сервере тоже: клиентская проверка — удобство, серверная — безопасность.

15. Продвинутые таблицы: сортировка, фильтры

Таблицы для админок редко живут без сортировки и фильтров. Включение — декларативное:

<el-table :data="rows" @sort-change="onSort">
  <el-table-column prop="price" label="Цена" sortable />
  <el-table-column label="Статус">
    <template #default v-if>
     &nbsp;
    </template>
  </el-table-column>
</el-table>
Сортировку и фильтрацию на больших данных делай на сервере, передавая параметры в запрос, — иначе браузер «съест» память.

16. Доступность и клавиатура

Element Plus заботится об a11y, но часть ответственности на тебе:

<el-dialog title="Редактировать" :close-on-click-modal="false"
            aria-labelledby="dlg-title">
  <h2 id="dlg-title">Редактирование записи</h2>
  ...
</el-dialog>
Пройдись по интерфейсу только клавиатурой (Tab + Enter): если упорядочил фокус — 80% задач a11y сделано.

17. Динамические формы и кастомная валидация

Формы с «полями по кнопке» (товары в заказе, вложения) собираются через массив полей и динамический rules:

<el-form :model="form" :rules="rules" ref="formRef">
  <div v-for="(line, i) in form.lines" :key="i">
    <el-form-item :prop="`lines.${i}.name`"
                  :rules="{ required: true, message: 'Укажите название' }">
      <el-input v-model="line.name" />
    </el-form-item>
  </div>
  <el-button type="primary" @click="addLine">+ Строка</el-button>
</el-form>
const validators = {
  minTotal: (rule, value, cb) =>
    value < 1 ? cb(new Error("Сумма не может быть пустой")) : cb(),
};
Динамические формы — основной источник багов: тестируй добавление, удаление и перезаполнение полей отдельно.

18. Кастомные колонки и экспорт данных

Таблицы редко показывают сырые поля. Слоты и форматтеры — базовый инструмент:

<el-table-column prop="price" label="Цена">
  <template #default="{ row }">
    {{ formatPrice(row.price) }}
  </template>
</el-table-column>
function exportCsv(ref) {
  const rows = ref.value.getSelectionRows().map(r => [r.title, r.price]);
  downloadCsv(rows);   // браузер сам скачает
}
Экспортируй только то, что видит пользователь: чувствительные поля не выгружай в файл.

19. Skeleton, Empty и состояния загрузки

Пользователь прощает паузу, но не белый экран. Специальные состояния закрывают UX-дыры:

<el-skeleton :loading="loading" animated>
  <template #template>
    <el-skeleton-item variant="h3" />
    <el-skeleton-item variant="rect" />
  </template>
  <template #default>
    <p>Список товаров…</p>
  </template>
</el-skeleton>

<el-empty v-if="!rows.length" description="Ничего не найдено">
  <el-button @click="reload">Обновить</el-button>
</el-empty>
Скелетон без анимации — просто серый фон: включай animated и согласуй с темой.

20. Большие списки: el-table-v2 и виртуализация

Десять тысяч строк на странице съедают память и рендер. Виртуализация рисует только видимое:

<el-table-v2 :columns="columns" :data="rows"
  :width="800" :height="400" fixed />
<el-select :remote-method="search" remote filterable
           v-model="user" :loading="selectLoading"> ... </el-select>
Виртуализация и фиксированная ширина ходят рука об руку: задай размеры колонок заранее.

21. Чек-лист UI перед продакшеном

UI-финал решает восприятие, но косяки ловятся позже. Пройди по списку:

  1. компоненты одной строки — одной высоты (form item, кнопки);
  2. пустые и ошибочные состояния прикрыты (el-empty, el-message, el-dialog с текстом);
  3. локализация: ConfigProvider с ru и кастомные тексты сообщений;
  4. тема: CSS-переменные переопределены глобально, а не точечно в компоненте;
  5. доступность: tab-порядок и aria-подписи на иконочных кнопках.
const app = createApp(App)
app.use(ElementPlus, { locale: ru })
app.mount("#app")
Прожми интерфейс на клавиатуре и маленьком экране: половина дефектов именно там.

Что дальше

Собери админку на Vue 3: таблица товаров, форма с валидацией, корзина в Pinia. Запросы к API — курс REST API, а тесты компонентов — на Vitest c @vue/test-utils.

← Вернуться к курсам