Расскажите об архитектуре крупных 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

&lt;Field name="email" as="input" rules="required|email" /&gt;
&lt;ErrorMessage name="email" /&gt;

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