Vue-костыли и странности: от классики до дичи (часть 2)

3. Setup-спагетти: блендер в «script setup»

Как часто встречается: Главный бич современных проектов на Vue 3.

Схема: 500+ строк кода сплошным полотном. Объявили стейт, тут же данные для формы, сетевой запрос, следом вотчер, тут же логика открытия попапа, снова логика формы… Всё перемешано в кашу, где связанные между собой переменне и функции непонятно. Выглядит примерно так: const userId = ref(1); const isPopupOpen = ref(false) - Попап const formData = reactive({ … }) - Форма watch(userId, () => { … }) - Слежка за юзером const submitForm = () => { … } - Снова форма const togglePopup = () => { … } - Снова попап onMounted(() => { fetchAnalytics() }) - Вообще аналитика

В чем боль: Свобода Composition API сыграла злую шутку. У такого кода нет внутренней архитектуры, его больно читать и поддерживать. Чтобы починить один несчастный инпут в форме, вам приходится бесконечно скроллить файл вверх-вниз, выуживая разбросанные куски стейта, вотчеров и функций среди логики попапов и аналитики.

Как надо: Группировать код по логическому смыслу и выносить крупные фичи в изолированные composables (кастомные хуки вроде useUserForm, useModal). Секция <script setup> в идеале должна быть чистым «дирижером», который просто импортирует блоки и связывает их с шаблоном. Может выглядеть примерно так:

// Логика формы const { formData, isSubmitting, submitForm } = useUserForm()

// Логика попапа const { isOpen: isPopupOpen, togglePopup } = useModal()

// Логика аналитики useAnalytics(userId)


4. Псевдо-DDD и матрёшки из папок

Как часто встречается: Любимая забава разработчиков, которые только что прочитали про модульную архитектуру, но ещё не набили шишек.

Схема: Папка в папке, папкой погоняет. Логика у автора простая: «Раз эта кнопка используется только внутри карточки, которая лежит в сетке каталога, значит, мы её на самый глубокий уровень и запрячем!». Имеем примерно такую структуру и имена компонентов длиной с немецкое существительное Catalog/Grid/Card/Btn/CatalogViewGridCardBtn.vue. А потом эту «кнопку каталога» импортируют на страницу «О нас» (AboutView.vue).

В чем боль: - Нарушается здравый смысл. Если компонент называется Catalog…Btn, но нужен на странице «О нас», то у него кривое имя и место жительства. Он перестал быть изолированным компонентом каталога. Либо это абсолютно базовая кнопка, которой не место в дебрях каталога, либо на странице «О нас» используется интерфейсный франкенштейн.

- Оверинжиниринг и когнитивная нагрузка. Чтобы просто поправить цвет текста на кнопке, разработчику приходится прокликать 5 уровней вложенности папок в IDE. Колоссальное количество ментальной энергии уходит просто на навигацию по проекту.

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

Как надо: - Атомарные / Shared компоненты. Все базовые UI-элементы (кнопки, инпуты, лоадеры) должны лежать в единой глобальной папке src/components/ui/ (или shared/ui). Они называются просто AppButton.vue или UiButton.vue и доступны везде.

- Плоская структура модулей: Внутри конкретной бизнес-страницы или фичи (например, Catalog) держите структуру папок плоской. Максимум один уровень вложенности. Если кнопка специфична для каталога (например, кнопка «Купить в 1 клик» со своей логикой запроса), пусть она лежит в Catalog/components/CatalogBuyButton.vue, но не зарывайте её на дно пятиуровневой матрешки.

А у вас есть “любимый” хак в Vue? Делитесь в комментариях!