Vue 3 для fullstack-разработчика: от нуля до рабочей админ-панели

Практический курс по Vue 3 для fullstack-разработчиков, которые хотят быстро внедрить фронтенд в свои проекты. Без лишней теории — только рабочий код: реактивность, Composition API, компоненты, Pinia, Vue Router и сборка админ-панели.

1. Быстрый старт и архитектура Vue 3

Быстрый старт и архитектура Vue 3

Представьте: вы только что получили заказ на MVP-приложение — клиентская панель с авторизацией, списком заказов и графиками. У вас есть бэкенд на Node или Laravel, и вам нужно «повесить» на него фронтенд за неделю. Vue 3 — один из немногих фреймворков, который позволяет fullstack-разработчику перейти от нуля к работающему интерфейсу за считанные часы, а не дни.

Почему Vue 3, а не React или Angular

Когда вы работаете fullstack, каждый час фронтенд-разработки — это час, который вы не тратите на бэкенд, DevOps или архитектуру. Vue 3 выигрывает здесь по нескольким причинам:

| Критерий | Vue 3 | React | Angular | |---|---|---|---| | Порог входа | Низкий — HTML-шаблоны | Средний — JSX | Высокий — TypeScript, DI | | Размер бандла | ~33 КБ (gzip) | ~42 КБ (gzip) | ~143 КБ (gzip) | | Интеграция с бэкендом | Через Vite за минуты | CRA/Vite | CLI-генерация | | Скорость прототипирования | Очень высокая | Высокая | Средняя |

Vue 3 не требует изучения нового синтаксиса шаблонов (как JSX в React) и не навязывает жёсткую архитектуру (как Angular). Вы пишете обычный HTML, добавляете директивы — и получаете реактивный интерфейс.

Установка и структура проекта

Для создания проекта используется Vite — сборщик, который заменил Webpack в экосистеме Vue. Он стартует за секунды вместо десятков секунд.

После выполнения этих команд вы получите работающий dev-сервер на http://localhost:5173. Откройте проект в редакторе — перед вами такая структура:

Ключевой файл — main.js. Это точка входа, где создаётся приложение Vue:

Функция createApp() создаёт экземпляр приложения, а .mount('#app') привязывает его к DOM-элементу с id="app" в index.html. Всё, что внутри этого элемента, управляется Vue.

Архитектура однофайлового компонента (SFC)

Каждый .vue-файл — это однофайловый компонент (Single File Component, SFC). Он объединяет три секции:

Три секции — это не просто удобство, а архитектурный принцип. Шаблон описывает что показать, скрипт описывает как работает, стили описывают как выглядит. Атрибут scoped в <style> гарантирует, что стили не «протекут» в другие компоненты.

> Ключевое отличие Vue от React: в Vue шаблон — это HTML с директивами, а не JavaScript-функция, возвращающая разметку. Это делает код более читаемым для тех, кто привык работать с HTML.

Реактивность в двух словах

Реактивность — это механизм, при котором изменение данных автоматически обновляет отображение в браузере. В Vue 3 она построена на JavaScript Proxy.

Когда вы меняете count.value или user.name, Vue автоматически обновляет все места в шаблоне, где эти данные используются. Вам не нужно вручную вызывать render() или setState() — это ключевое отличие от ванильного JavaScript.

Мы подробно разберём реактивность в следующей статье, а пока достаточно понимать: данные в Vue — это не просто переменные, а «умные» объекты, которые отслеживают своё изменение.

Директивы — связка данных и DOM

Директивы — это специальные атрибуты Vue, которые начинаются с префикса v-. Они связывают данные с HTML-разметкой.

Сокращения: :src вместо v-bind:src, @click вместо v-on:click. В реальном коде вы почти всегда будете использовать короткую форму.

Жизненный цикл компонента

Каждый компонент проходит через стадии: создание → монтирование в DOM → обновление → уничтожение. Vue предоставляет хуки жизненного цикла (lifecycle hooks), чтобы вы могли выполнять код на каждой стадии.

Для fullstack-разработчика самый важный хук — onMounted. Именно здесь вы будете загружать данные с бэкенда, инициализировать графики и настраивать WebSocket-подключения.

Первое приложение: счётчик с запросом к API

Закрепим на практике. Создадим компонент, который загружает список пользователей с публичного API и отображает его:

Этот компонент демонстрирует три ключевых паттерна, которые вы будете использовать в каждом проекте: реактивные переменные (ref), загрузку данных при монтировании (onMounted + fetch) и условный рендеринг (v-if / v-else).

Настройка окружения для production

Когда прототип готов, соберите проект для продакшена:

Vite создаст оптимизированные файлы в папке dist/. Эти файлы — статика, которую можно отдать через Nginx, S3 или любой статический хостинг. Для интеграции с бэкендом на Laravel или Express достаточно настроить проксирование API-запросов.

В vite.config.js можно задать базовый путь и прокси:

Теперь запросы на /api/users в dev-режиме будут автоматически перенаправляться на ваш бэкенд на порту 3000 — без проблем CORS.

Что мы получили

На этом этапе у вас есть работающее Vue 3-приложение с Vite, вы понимаете структуру SFC-компонентов, знаете базовые директивы и жизненный цикл. Этого достаточно, чтобы начать строить интерфейс для любого бэкенда. В следующей статье мы погрузимся в Composition API и разберём, как управлять сложной реактивностью — основу любого административного интерфейса.

2. Composition API и реактивность данных

Composition API и реактивность данных

В предыдущей статье вы создали первый компонент и увидели, как ref автоматически обновляет DOM. Но настоящая сила Vue 3 раскрывается, когда данных становится много: формы с десятками полей, таблицы с фильтрацией, связанные селекты. Без глубокого понимания реактивности такой код превращается в хаос. Composition API — это набор функций, который даёт полный контроль над реактивностью и позволяет структурировать логику по функциональности, а не по опциям.

ref vs reactive: когда что использовать

В Vue 3 есть два способа создать реактивные данные, и выбор между ними — не формальность.

ref() оборачивает любое значение в реактивный объект с полем .value:

reactive() создаёт реактивную копию объекта или массива:

Вот практическое правило, которое сэкономит вам часы отладки:

| Ситуация | Используйте | |---|---| | Примитив (строка, число, булево) | ref | | Объект формы с известной структурой | reactive | | Массив элементов из API | ref | | Переменная, которую будете передавать в watch | ref | | Глубоко вложенные объекты | reactive |

Почему для массивов лучше ref? Потому что при замене массива целиком (items.value = newData) реактивность сохраняется. С reactive вы потеряете реактивность, если переприсвоите переменную:

computed: вычисляемые свойства

computed — это реактивное значение, которое автоматически пересчитывается при изменении своих зависимостей. Это не функция, которую вы вызываете, а реактивная переменная, которая сама следит за данными.

Когда вы меняете selectedCategory, filteredProducts и totalPrice пересчитываются автоматически. Vue отслеживает зависимости внутри computed и обновляет результат только когда одна из них изменилась — кеширование встроено.

> computed — это замена для всех вычислений, которые вы раньше делали в watch или вручную. Если значение можно вычислить из других данных — используйте computed, а не watch.

watch и watchEffect: наблюдение за изменениями

watch — явное наблюдение за конкретным источником. Вы указываете, за чем следить и что делать при изменении:

Когда использовать watch, а когда watchEffect?

  • watch — когда вам нужен доступ к предыдущему значению (oldQuery), когда нужно явно контролировать зависимости, или когда источник — геттер функции.
  • watchEffect — когда логика простая и зависимости очевидны из тела функции.
  • Извлечение логики в композабли (Composables)

    Вот где Composition API раскрывает своё главное преимущество. Когда логика растёт, вы выносите её в отдельные функции — композабли (composables). Это аналог хуков в React, но без ограничений порядка вызовов.

    Представьте: у вас на трёх разных страницах админки нужно загружать данные с пагинацией, обрабатывать ошибки и показывать спиннер. Без композаблей вы будете копировать один и тот же код. С композаблем — пишете один раз:

    Теперь используйте его в любом компоненте:

    Композабли могут содержать computed, watch, хуки жизненного цикла — всё, что доступно в setup. Это делает их полноценными единицами логики.

    Ещё один практичный пример — композабль для управления модальными окнами:

    toRefs и деструктуризация реактивных объектов

    Частая ловушка: вы деструктурируете reactive-объект и теряете реактивность:

    Решение — toRefs:

    Это особенно полезно при возврате данных из композаблей: вы возвращаете toRefs(state), и компонент может деструктурировать их без потери реактивности.

    Практический пример: фильтр + таблица

    Соберём всё вместе. Компонент админ-панели с фильтрацией, сортировкой и пагинацией:

    Обратите внимание: вся логика фильтрации, сортировки и пагинации — это цепочка computed-свойств. Каждое следующее зависит от предыдущего, и Vue автоматически пересчитывает только то, что изменилось. Ни одного ручного updateTable() — реактивность делает всё за вас.

    Что мы получили

    Теперь вы понимаете разницу между ref и reactive, умеете создавать вычисляемые свойства, наблюдать за изменениями и извлекать повторяющуюся логику в композабли. Это фундамент, на котором строится любой интерфейс админ-панели. В следующей статье мы разберём, как превратить такие компоненты в переиспользуемые блоки — кнопки, модалки, таблицы — которые можно использовать на всех страницах проекта.

    3. Создание переиспользуемых компонентов

    Создание переиспользуемых компонентов

    Когда вы строите админ-панель, на каждой странице появляются одни и те же элементы: кнопки, модальные окна, таблицы, формы ввода, тосты с уведомлениями. Если писать их заново каждый раз, проект разрастается, а единообразие интерфейса теряется. Переиспользуемые компоненты — это решение: вы пишете один раз, настраиваете через пропсы и события, и используете повсюду.

    Пропсы: передача данных внутрь компонента

    Пропсы (props) — это входные параметры компонента. Родитель передаёт данные через атрибуты, а дочерний компонент объявляет, какие пропсы он ожидает.

    Теперь v-model работает на вашем компоненте так же, как на нативном <input>:

    В Vue 3.4+ доступен макрос defineModel, который упрощает эту связку:

    Паттерн emit: дочерний компонент сообщает о событиях

    Компоненты не должны напрямую менять данные родителя. Вместо этого они эмитят события, а родитель решает, что делать:

    Родитель подписывается на события:

    Этот паттерн называется однонаправленный поток данных: данные идут сверху вниз (через пропсы), а события — снизу вверх (через emit). Компонент остаётся предсказуемым и тестируемым.

    Практика: библиотека базовых компонентов

    Для админ-панели вам понадобится набор базовых компонентов. Вот минимальный список с файловой структурой:

  • base/ — атомарные компоненты без бизнес-логики. Используются на всех страницах.
  • layout/ — компоненты макета: шапка, боковая панель, общий layout.
  • features/ — компоненты с бизнес-логикой, специфичные для вашего приложения.
  • Глобальная регистрация базовых компонентов избавляет от повторных импортов:

    После этого BaseButton, BaseInput и BaseModal доступны в любом компоненте без импорта.

    Типичные ошибки при создании переиспользуемых компонентов

    Слишком много пропсов. Если компонент принимает 15+ пропсов, он делает слишком много. Разбейте его или используйте слоты для кастомизации.

    Мутация пропсов. Никогда не изменяйте пропс напрямую внутри дочернего компонента. Если нужно локальное значение — создайте копию:

    Отсутствие fallback в слотах. Всегда提供айте содержимое по умолчанию в слотах, чтобы компонент работал, даже если родитель не предоставил контент.

    Что мы получили

    Вы научились создавать компоненты с пропсами, слотами и событиями — строительные блоки любого интерфейса. У вас есть инструменты для построения библиотеки UI-компонентов, которая будет единообразно выглядеть и предсказуемо работать. В следующей статье мы подключим Pinia для управления состоянием и научимся работать с API так, чтобы данные были доступны из любого компонента приложения.

    4. Управление состоянием с Pinia и работа с API

    Управление состоянием с Pinia и работа с API

    В админ-панели данные разбросаны по всему приложению: текущий пользователь нужен в шапке и в настройках, список товаров — в таблице и в фильтрах, корзина — в сайдбаре и на странице оформления. Передавать всё через пропсы от компонента к компоненту — путь к «аду пропсов» (prop drilling). Pinia — официальный менеджер состояния Vue 3 — решает эту проблему: данные хранятся в одном месте и доступны отовсюду.

    Зачем нужен Pinia, если есть provide/inject

    Vue предлагает встроенный механизм provide/inject для передачи данных без пропсов. Но у него есть ограничения: нет отслеживания изменений из DevTools, нет встроенных действий с асинхронной логикой, сложно отлаживать поток данных в большом приложении.

    Pinia даёт:

  • Глобальное хранилище с реактивными данными
  • DevTools-интеграцию — вы видите каждое изменение состояния
  • Типизацию через TypeScript без дополнительных усилий
  • Модульность — каждое хранилище независимо
  • Поддержку SSR из коробки
  • Установка и настройка

    После этого вы можете создавать хранилища (stores).

    Создание хранилища: три подхода

    Setup-синтаксис (рекомендуемый)

    Использует Composition API — тот же синтаксис, что вы уже знаете:

    Когда НЕ нужен Pinia

    Не каждое состояние нужно выносить в глобальное хранилище. Локальное состояние формы, UI-состояние модального окна, локальная фильтрация — всё это лучше оставить в компоненте или композабле.

    Правило: если данные используются только в одном компоненте — оставляйте локально. Если данные нужны в двух и более местах или переживают жизненный цикл компонента (например, данные пользователя после перехода между страницами) — выносите в Pinia.

    Что мы получили

    Теперь у вас есть полноценная система управления состоянием: хранилища для авторизации, сущностей и уведомлений, паттерны CRUD-операций и оптимистичных обновлений. Данные стали доступны отовсюду без пропсов. В следующей статье мы соберём всё вместе: настроим маршрутизацию с Vue Router, создадим layout админ-панели и защитим маршруты навигационными гардами.

    5. Маршрутизация и создание админ-панели

    Маршрутизация и создание админ-панели

    Вы освоили реактивность, компоненты и управление состоянием. Но без маршрутизации у вас одностраничное приложение, которое показывает всё сразу. Vue Router превращает его в многостраничное приложение с чистыми URL, навигацией и защитой страниц — именно то, что нужно для админ-панели.

    Установка и базовая конфигурация

    Создайте файл маршрутизации:

    Подключите в main.js:

    Ключевые моменты этой конфигурации

    Ленивая загрузка через () => import(...). Код каждой страницы загружается только при первом переходе. Для админ-панели с десятками страниц это критично — пользователь не скачивает весь код сразу.

    Вложенные маршруты (children). Все защищённые страницы — дочерние к маршруту /, который использует компонент AppLayout. Это значит, что layout с шапкой и сайдбаром отображается один раз, а внутри него меняется только содержимое страницы через <router-view />.

    Динамический сегмент :id в маршруте UserEdit. Параметр доступен через route.params.id или через пропс благодаря props: true.

    Catch-all маршрут /:pathMatch(.) в конце списка перехватывает все несуществующие URL и показывает страницу 404.

    Layout админ-панели

    Компонент AppLayout — это каркас, который виден на всех защищённых страницах:

    Элемент <router-view /> — это точка, куда Vue вставляет компонент текущего маршрута. Всё, что вокруг него (сайдбар, шапка), остаётся на месте при навигации. Именно поэтому переход между страницами происходит мгновенно без перезагрузки.

    Навигационные гарды: защита маршрутов

    Навигационные гарды (navigation guards) — это функции, которые перехватывают навигацию и решают, разрешить переход, перенаправить или отменить его. Для админ-панели это механизм защиты страниц от неавторизованного доступа.

    Глобальный гард beforeEach

    Этот гард выполняется перед каждым переходом. Логика:

  • Если маршрут требует авторизации (requiresAuth: true), а пользователь не залогинен — редирект на логин с параметром redirect, чтобы после входа вернуться обратно.
  • Если маршрут только для гостей (guest: true), а пользователь уже залогинен — редирект на дашборд.
  • Если маршрут ограничен по ролям — проверяем userRole.
  • Обработка редиректа после логина

    Параметр route.query.redirect содержит путь, с которого пользователь был перенаправлен на логин. После успешного входа он возвращается именно туда — на страницу, которую хотел открыть.

    Программная навигация

    Помимо <router-link>, вы можете управлять навигацией из кода:

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

    Хлебные крошки и метаданные маршрутов

    Объект meta маршрута — это хранилище произвольных данных, которые вы можете использовать в любом месте приложения. Помимо requiresAuth и roles, полезно хранить заголовок страницы:

    Автоматическое обновление заголовка вкладки:

    Генерация хлебных крошек на основе matched:

    Параллельные маршруты (Named Views)

    Иногда на одной странице нужно показать несколько независимых компонентов, определяемых маршрутом. Например, дашборд с основным контентом и боковой панелью:

    В layout разместите два <router-view>:

    Сборка админ-панели: финальная структура

    Каждый элемент на своём месте: роутер определяет структуру страниц, layout обеспечивает единообразный вид, хранилища управляют данными, а компоненты отвечают за отображение.

    Защита данных: гарды — это не безопасность

    Важно понимать: навигационные гарды блокируют отображение страницы, но не защищают данные. Если ваш API возвращает чувствительные данные без проверки токена на сервере, пользователь может получить к ним доступ напрямую через DevTools или curl.

    Гарды — это UX-защита: пользователь не видит интерфейс, к которому у него нет доступа. Реальная авторизация должна быть на бэкенде: каждый API-запрос проверяет токен и роль, и возвращает 401/403 при отсутствии прав.

    Что мы получили

    За пять статей вы прошли путь от npm create vite до работающей админ-панели с авторизацией, защищёнными маршрутами, глобальным состоянием и переиспользуемыми компонентами. Это не учебный проект — это архитектура, которую вы можете применить к любому реальному проекту. Каждый следующий шаг — добавление новых страниц, интеграция с API, настройка CI/CD — строится на фундаменте, который вы заложили.