Расскажите об архитектуре крупных Vue-приложений, которую вы реализовывали.
Архитектура крупных Vue-приложений выстраивается на основе модульности, масштабируемости, изоляции слоёв ответственности и строгой структуры каталогов. При проектировании таких приложений учитываются требования к производительности, асинхронной загрузке, совместной работе над кодом, повторному использованию компонентов и безопасности. Ниже — подробное описание архитектуры, реализованной на реальных крупных Vue 3-проектах с использованием Pinia, Vue Router, TypeScript, Vite и REST API.
1. Общая структура каталогов
src/
├── app/ # корневая инициализация приложения (app.ts, plugins)
├── assets/ # глобальные стили, изображения
├── components/ # общие (shared) компоненты
├── features/ # бизнес-модули с изоляцией (feature-first)
│ └── user/
│ ├── components/
│ ├── pages/
│ ├── store/
│ └── api/
├── pages/ # страницы и роутовые компоненты
├── entities/ # атомарные логики: пользователь, товар и т.д.
├── shared/ # UI-библиотека, утилиты, константы
├── widgets/ # композитные UI-виджеты из features/entities
├── router/ # конфигурация маршрутов
├── store/ # глобальные сторы (если необходимо)
├── types/ # глобальные типы и интерфейсы
└── utils/ # утилиты общего назначения
Такой подход реализует принципы Feature-Sliced Design (FSD), где бизнес-логика изолируется от UI и масштабируется независимо.
2. Инициализация приложения
В точке входа main.ts запускается создание приложения, подключение плагинов, маршрутов и стора.
// src/main.ts
import { createApp } from 'vue';
import { createPinia } from 'pinia';
import App from './App.vue';
import { router } from './router';
const app = createApp(App);
app.use(createPinia());
app.use(router);
app.mount('#app');
Плагины и глобальные конфигурации выносятся в app/plugins/ для централизованной регистрации:
// app/plugins/axios.ts
import axios from 'axios';
export const http = axios.create({
baseURL: import.meta.env.VITE_API_BASE
});
3. Разделение по слоям
Shared
-
Компоненты: UiButton.vue, UiInput.vue, UiModal.vue
-
Стили: глобальные переменные (SCSS/LESS/CSS variables)
-
Константы: ROUTES, REGEX, COLORS
-
Хелперы: debounce(), formatDate()
Entities
Минимальные бизнес-сущности. Например, пользователь — это types/User.ts, store/user.ts, api/user.ts.
// entities/user/api/user.api.ts
import { http } from '@/app/plugins/axios';
export const getUserById = (id: string) => http.get(\`/users/${id}\`);
4. Работа с маршрутизацией
Vue Router настраивается в router/index.ts. Используются:
-
Lazy-загрузка маршрутов через defineAsyncComponent
-
Роли и доступ через глобальные guards
-
Layout-системы (main, auth, dashboard)
// router/index.ts
import { createRouter, createWebHistory } from 'vue-router';
const routes = \[
{
path: '/',
component: () => import('@/pages/HomePage.vue')
},
{
path: '/dashboard',
component: () => import('@/layouts/DashboardLayout.vue'),
children: \[
{
path: 'profile',
component: () => import('@/features/profile/pages/ProfilePage.vue')
}
\]
}
\];
export const router = createRouter({
history: createWebHistory(),
routes
});
5. Работа с состоянием
Используется Pinia для управления состоянием. Разделение стора:
-
features/user/store/useUserStore.ts — бизнес-хранилище
-
store/ui.ts — глобальное UI-состояние (например, текущая тема)
// useUserStore.ts
export const useUserStore = defineStore('user', {
state: () => ({
profile: null
}),
actions: {
async fetchProfile() {
const { data } = await getUserById('me');
this.profile = data;
}
}
});
Pinia интегрируется с devtools, поддерживает SSR, TypeScript и предоставляет реактивность без mapState и mapGetters.
6. API-слой
REST API или GraphQL разбиваются на изолированные модули внутри features/*/api.
-
Все запросы идут через инстанс axios, с подключёнными интерцепторами (auth, error-handling)
-
Ошибки логируются через глобальные обработчики (например, Sentry)
http.interceptors.response.use(
response => response,
error => {
if (error.response.status === 401) {
router.push('/login');
}
return Promise.reject(error);
}
);
7. Асинхронная загрузка и код-сплиттинг
Каждая страница, компонент или модуль загружается по требованию:
component: () => import('@/features/settings/pages/SettingsPage.vue')
Также используется defineAsyncComponent для ленивой инициализации крупных UI-модулей:
const LazyModal = defineAsyncComponent(() =>
import('@/widgets/modals/BigModal.vue')
);
8. Тестирование
-
Unit-тесты: Vitest или Jest
-
E2E: Cypress или Playwright
-
Локаторы определяются через data-testid
-
Snapshot-тесты используются для UI
describe('LoginForm', () => {
it('renders correctly', () => {
const wrapper = mount(LoginForm);
expect(wrapper.find('input\[type="email"\]').exists()).toBe(true);
});
});
9. SSR и Hydration
Если требуется SSR, структура приложения включает серверную часть. Используется Nuxt или Vite SSR API. Поддерживаются:
-
async setup() для получения данных до рендера
-
передача initialState через window.__INITIAL_STATE__
Клиентская и серверная части синхронизируются через гидратацию.
10. Интернационализация (i18n)
Подключается vue-i18n, перевод храним в JSON-файлах:
locales/
├── en.json
├── ru.json
Пример использования:
<p>{{ $t('login.title') }}</p>
11. Layout-система
Обычно используются несколько layout-обёрток: DefaultLayout, AuthLayout, AdminLayout.
Они управляют шапкой, меню, футером, контентной областью.
<template>
<Header />
<main><slot /></main>
<Footer />
</template>
12. Уведомления и диалоги
Используется глобальный менеджер уведомлений через provide/inject или плагин (например, Toastification).
toast.success('Успешно сохранено');
Диалоги реализованы через динамический рендеринг компонента:
openModal(ConfirmDialog, { props: { message: 'Удалить?' } });
13. Работа с формами
Используются:
-
vee-validate для валидации
-
yup для схем
-
Абстракции над <input> и <select> для унификации UI
<Field name="email" as="input" rules="required|email" />
<ErrorMessage name="email" />
14. Архитектура событий
-
События через emit, v-model, либо глобальные eventBus (mitt)
-
Часто реализуются с помощью паттернов pub/sub для диалогов и уведомлений
-
Сложные зависимости (например, синхронизация между вкладками) решаются через BroadcastChannel, localStorage, WebSocket
15. CI/CD и code quality
-
ESLint + Prettier + Stylelint
-
Husky + Lint-staged для pre-commit хуков
-
CI через GitHub Actions или GitLab CI
-
Docker-контейнеризация с nginx или node:alpine
Такой подход позволяет изолировать бизнес-логику, переиспользовать UI-компоненты, упрощать тестирование и поддерживать крупные команды разработчиков с минимальным конфликтом.