Правильная файловая структура — скелет любого фронтенд-приложения. В Vue 3 нет строгих предписаний, как раскладывать файлы по папкам, кроме базового разделения components/, views/. Но с ростом проекта хаотичное размещение кода быстро превращается в проблему. В этой статье разберём популярные подходы к организации Vue-проектов: от простейшего плоского до микрофронтендов.

Плоская структура: быстрота и минимализм

С этой схемы стартуют почти все небольшие проекты (пет-приложения). Суть в том, что все компоненты лежат в одной-двух папках, а утилиты и композаблы сгруппированы по назначению, без дополнительных уровней вложенности.
/src|-- /components| |-- BaseButton.vue| |-- BaseModal.vue| |-- ProductCard.vue|-- /composables| |-- useCart.js| |-- useProducts.js|-- /utils| |-- formatters.js|-- /layouts| |-- MainLayout.vue| |-- AuthLayout.vue|-- /plugins| |-- analytics.js|-- /views| |-- HomePage.vue| |-- ProductPage.vue|-- /router| |-- index.js|-- /store| |-- index.js|-- /assets|-- /tests|-- App.vue|-- main.js
Плюсы:
-
Минимум настройек
-
Быстрый старт
-
Низкий порог входа
Минусы:
-
Не масштабируется
-
Быстро возникает дублирование
-
Сложно искать файлы при росте >30 компонентов
Когда использовать: демо, обучающие проекты.
Атомарный дизайн (Atomic Design)

Представьте, что ваш интерфейс — это живой организм, собранный из мельчайших «клеток». В масштабных проектах на Vue такой взгляд помогает держать хаос под контролем. Методология Atomic Design предлагает разложить любой экран на пять осмысленных уровней — от неделимых частиц до полноценных рабочих страниц:
-
Атомы — фундаментальные кирпичики интерфейса, которые уже нельзя разбить на более мелкие части без потери смысла. В мире Vue это одиночные элементы: кнопка, поле ввода, иконка, заголовок. Они абстрактны, чисты и не зависят от контекста.
-
Молекулы — первые полезные конструкции, рождающиеся при объединении нескольких атомов. Так, текстовое поле с кнопкой и меткой превращается в строку поиска. Молекулы уже начинают решать конкретные микро-задачи, оставаясь при этом переиспользуемыми.
-
Организмы — самостоятельные, визуально законченные блоки интерфейса, собранные из молекул и/или атомов. Например, шапка сайта, в которой живут логотип (атом), строка поиска (молекула) и навигационное меню из кнопок-атомов. Такой компонент уже определяет характер целого участка макета.
-
Шаблоны — скелет страницы, где организмы расставляются по сетке, образуя абстрактную композицию. Здесь появляются отступы, колонки и логическая структура, но данные пока условные или отсутствуют — это как архитектурный макет здания без мебели и жильцов.
-
Страницы — конечный уровень, где шаблон наполняется реальным контентом, живыми данными из API, состояниями загрузки и ошибками. Именно здесь интерфейс становится тем, что видит пользователь, и именно на этом уровне видны все нюансы работы системы.
Такой подход превращает хаотичный набор компонентов в стройную экосистему, где каждому элементу находится своё место, а внесение изменений на любом уровне автоматически отражается по всей иерархии — но всегда предсказуемо и безболезненно.
/src|-- /components| |-- /atoms| | |-- UiBadge.vue| | |-- UiAvatar.vue| |-- /molecules| | |-- SearchBar.vue| | |-- CartItem.vue| |-- /organisms| | |-- ProductCard.vue| |-- /templates| | |-- ProductListTemplate.vue|-- /pages| |-- HomePage.vue|-- /composables| |-- useProducts.js|-- /utils| |-- priceHelpers.js|-- /layouts| |-- DefaultLayout.vue|-- /router| |-- index.js|-- /store| |-- index.js|-- App.vue|-- main.js
Плюсы: атомы и молекулы отлично переиспользуются. Если дизайнеры меняют кнопку — достаточно поправить один компонент, и изменения распространятся по всему приложению.
Минусы: постоянная мысленная нагрузка «а это атом или молекула?» замедляет разработку. Часто возникает жёсткая связанность на уровне шаблонов, а бизнес-логика размазывается по слоям, что усложняет рефакторинг.
Модульная архитектура

Модульный подход группирует файлы не по технической роли, а по предметной области. Каждый функциональный блок (модуль) полностью инкапсулирует свою логику: компоненты, хранилище, API-клиенты, тесты и даже стили. Это похоже на разделение кода по микросервисам, но внутри одного репозитория.
/src|-- /core // App Shell и общие ресурсы| |-- /components| | |-- BaseButton.vue| |-- /models| |-- /store // Глобальное состояние (сессия, уведомления)| |-- /services // API-клиент, интерсепторы| |-- /views| | |-- AdminLayout.vue| |-- /utils| | |-- validators.js|-- /modules| |-- /products // Модуль «Каталог товаров»| | |-- /components| | | |-- ProductThumbnail.vue| | | |-- ProductCard.vue| | |-- /models| | |-- /store| | | |-- productStore.js| | |-- /services| | | |-- productApi.js| | |-- /views| | | |-- ProductDetailPage.vue| | |-- /tests| | | |-- productTests.spec.js| |-- /cart // Модуль «Корзина»| | |-- /components| | | |-- CartIcon.vue| | | |-- CartItem.vue| | |-- /store| | | |-- cartStore.js| | |-- /services| | |-- /views| | | |-- CartPage.vue| | |-- /tests| |-- /account // Модуль «Личный кабинет»| | |-- /components| | | |-- ProfileCard.vue| | |-- /store| | | |-- accountStore.js| | |-- /services| | |-- /views| | | |-- ProfilePage.vue|-- /assets| |-- /images| |-- /styles|-- /plugins| |-- translate.js|-- App.vue|-- main.js
Плюсы:
-
Слабая связанность. Модуль
cartиспользует API корзины и внутреннее хранилище, ничего не зная о деталях модуляproducts. Вы можете переписать логику товаров, не затронув корзину. -
Колокация ответственности. Разработчик, отвечающий за продуктовый каталог, видит все файлы в одной папке, что ускоряет навигацию и рефакторинг.
-
Масштабирование команд. Если над проектом работают несколько разработчиков, каждый может вести свой модуль практически без конфликтов слияния.
-
Миграция в микрофронтенды. Когда модуль разрастается до такого объёма, что его выгодно развернуть независимо, он уже изолирован по коду, остаётся лишь наладить его отдельную сборку и деплой.
Feature-Sliced Design (FSD)

FSD — более формализованный архитектурный подход, который разбивает приложение на слои по степени близости к бизнес-логике. Он пришёл из React-экосистемы, но применим и во Vue.
Слои FSD (снизу вверх):
-
Shared — переиспользуемый код без бизнес-логики (UI-кит, утилиты, API-клиент)
-
Entities — бизнес-сущности (пользователь, товар, заказ)
-
Features — пользовательские сценарии (добавление в корзину, авторизация)
-
Widgets — самостоятельные блоки (шапка, список товаров, профиль)
-
Pages — страницы приложения
-
App — глобальные настройки, провайдеры, стили
Правила архитектуры:
-
Код может импортироваться только снизу вверх
-
Запрещены импорты с того же или более высоких слоев
-
Shared можно использовать везде
-
Entities не знают о Features и выше
/src|-- /app| |-- App.vue| |-- main.js| |-- providers/| | |-- RouterProvider.vue| | |-- ThemeProvider.vue| |-- styles/| | |-- index.css|-- /pages| |-- /home| | |-- HomePage.vue| | |-- components/| | |-- model/| | |-- lib/| |-- /catalog| | |-- CatalogPage.vue| | |-- components/| | |-- model/| | |-- lib/|-- /widgets| |-- /header| | |-- Header.vue| | |-- components/| | |-- model/| |-- /footer| | |-- Footer.vue| | |-- components/| |-- /product-list| | |-- ProductList.vue| | |-- components/| | |-- model/|-- /features| |-- /auth| | |-- /login| | | |-- LoginForm.vue| | | |-- model/| | | |-- lib/| | |-- /registration| | | |-- RegistrationForm.vue| | | |-- model/| | |-- /logout| | |-- LogoutButton.vue| |-- /product| | |-- /add-to-cart| | | |-- AddToCartButton.vue| | | |-- model/|-- /entities| |-- /product| | |-- ui/| | | |-- ProductCard.vue| | | |-- ProductImage.vue| | | |-- ProductPrice.vue| | |-- model/| | | |-- types.ts| | | |-- selectors.js| | | |-- api.js| | |-- lib/| | |-- helpers.js| |-- /user| | |-- ui/| | | |-- UserAvatar.vue| | | |-- UserInfo.vue| | |-- model/| | | |-- types.ts| | | |-- selectors.js| | | |-- api.js|-- /shared| |-- /ui| | |-- /button| | | |-- Button.vue| | | |-- Button.spec.js| | |-- /input| | | |-- Input.vue| |-- /lib| | |-- api/| | | |-- apiClient.js| | | |-- endpoints.js| | |-- utils/| | | |-- formatters.js| | | |-- validators.js| | | |-- constants.js| | |-- hooks/| | |-- useDebounce.js| | |-- useLocalStorage.js| |-- /config| | |-- theme.js| |-- /assets| |-- /images| |-- /fonts| |-- /icons|-- /tests| |-- mocks/|-- router.js|-- store.js|-- main.js
Слои строго регламентируют импорты: элементы с нижнего слоя не могут обращаться к элементам верхнего. Это защищает от циклических зависимостей, но требует уверенного знания методологии всей командой. Для средних проектов и небольших команд такой уровень формализации часто избыточен — та же инкапсуляция достигается проще через модульную структуру.
Микрофронтенды

Микрофронтенды переносят философию микросервисов на клиентскую часть. Каждый раздел магазина — каталог, корзина, личный кабинет — разрабатывается и деплоится независимо, возможно, разными командами и на разных стеках.
Основные элементы:
-
Оболочка (Shell): координация маршрутизации, базовый каркас.
-
Независимые мини-приложения: Product Listing, Checkout, User Account.
Это серьёзный инструмент, где над фичами работают десятки разработчиков. Для подавляющего большинства приложений сложность деплоя, версионирования и отладки микрофронтендов не окупается.
Плюсы:
-
Независимое развертывание
-
Разные технологии в разных модулях
-
Масштабирование команд
-
Изоляция ошибок
Минусы:
-
Сложность настройки
-
Дублирование зависимостей
-
Сложность отладки
Что же выбрать для Vue 3-проекта?

Представь, что проект Vue — это большой шкаф с вещами. В начале вещей мало, их можно сложить как попало. Но когда проект растёт, нужна система хранения, чтобы быстро находить то, что относится к «корзине», «каталогу», «авторизации». Модульная архитектура — это способ разложить код по папкам-модулям так, чтобы в каждой лежало всё, что касается отдельной бизнес-задачи. Вот почему.
-
Она органично вписывается в экосистему Vue. Composition API со своими композаблами идеально сочетается с изолированными модулями. В одном модуле вы держите и презентационные компоненты, и логический слой (
useProduct,useCart,useAuth), и стор (Pinia-хранилища, если необходим модульный стейт). Никаких дополнительных абстракций не требуется. -
Инкрементальное внедрение. Вы можете начать с плоской структуры и безболезненно выделить модули по мере кристаллизации бизнес-требований. Уже работающий код просто переезжает в
/modules/…, без переписывания всей архитектуры. -
Совместимость с другими подходами. Если команда дизайна настаивает на атомарном дизайне, реализуйте его внутри
core/components/и, скажем, внутриmodules/products/components/. Если позже потребуется жёсткий FSD для части приложения — вы вольны организовать так отдельный модуль. -
Живой пример. Представьте, что в модуле
cartпонадобилось добавить промокоды. Вы создаёте подпапкуmodules/cart/composables/usePromocode.js, компонентPromocodeInput.vueи тест. Вся функциональность замкнута в одной ветке, не затрагивая каталог или профиль. Через месяц, когда корзина разовьётся до отдельного микрофронтенда, вы просто настраиваете для папкиmodules/cartотдельный Webpack/Vite-entry. Код менять не придётся.

P.S. И напоследок, чтобы ни у кого не возникло желания накинуться с критикой: это не истина в последней инстанции. Это исключительно мои мысли, основанные на моём опыте, наблюдениях и чувствах. Я не претендую на то, чтобы переубедить всех. Моё мнение — всего лишь одно из многих, но оно имеет полное право на существование, как и ваше. Если у вас иначе — замечательно, делитесь в комментариях конструктивно. Но без негатива, пожалуйста)
ссылка на оригинал статьи https://habr.com/ru/articles/1033300/