13 KiB
Стандарт написания фронтенда
Статус: обязательный стандарт.
Этот документ — единственный источник правил для нового и изменяемого кода в
evening_detective_frontend. Существующий код не переписывается только ради
соответствия стандарту: при изменении файла применяй правила к затронутому коду
и не расширяй diff без необходимости.
1. Базовые решения
- Стек: Vue 3,
<script setup lang="ts">, TypeScript, Vite, Pinia, Vue Router, Naive UI и@vicons. - UI-тексты пишутся на русском.
- Новые зависимости, альтернативные UI-библиотеки и новый state manager не добавляются без явного разрешения пользователя.
- Сначала переиспользуй существующий компонент, composable, util, store или токен. Новая абстракция нужна только при повторном использовании или когда без неё нарушается граница ответственности.
- Не добавляй код «на будущее», универсальные обёртки с одним потребителем и дублирующие типы.
2. Куда класть новый код
| Что добавляется | Единственное основное место | Правило |
|---|---|---|
| Маршрутная страница | src/views/*View.vue |
Только подключает страницу из components и не содержит бизнес-логики. |
| Экран или крупный блок страницы | src/components/*Page.vue |
Собирает экран, управляет его flow и передаёт данные дочерним компонентам. |
| Переиспользуемый визуальный блок | src/components/*.vue |
Не знает о конкретном маршруте; получает данные через props и сообщает событиями. |
| Логика нескольких компонентов | src/composables/use*.ts |
Состояние и lifecycle-композиция Vue; один composable — одна задача. |
| Чистая функция без Vue-состояния | src/utils/*.ts |
Форматирование, преобразование и вычисления; без компонентов и store. |
| Общее состояние | src/stores/*.ts |
Только данные и действия с общим жизненным циклом. |
| Тип доменной модели или редактора | src/types/*.ts |
Только типы приложения, которых нет в generated API. |
| API-вызов | src/api/*.ts или store/page flow |
Через сгенерированный клиент и существующий транспорт. |
| Сгенерированный API | src/api/generated/** |
Только результат npm run proto:gen; ручные правки запрещены. |
| Глобальный CSS и токен | src/assets/base.css / src/assets/main.css |
Только при действительно глобальном эффекте. |
| Стиль компонента | <style scoped> компонента |
Одноразовый стиль не выносить в глобальный CSS. |
Если код подходит под несколько строк, выбирай первую подходящую границу сверху.
Нельзя прятать бизнес-логику в utils, API-вызовы в визуальном компоненте или
локальное состояние в глобальном store.
3. Компоненты Vue
Каждый компонент имеет одну ответственность и читается сверху вниз: импорты;
props, emits и локальные типы; state и computed; обработчики и
асинхронные действия; template; scoped style.
- Используй
<script setup lang="ts">. - Props и emits всегда типизируй. Для текущего проекта допустимы typed
definePropsиObject as PropType<T>. - Производные значения оформляй через
computed, не дублируй их вref. - Локальное состояние держи в компоненте; родитель передаёт данные через props, ребёнок сообщает намерение через emits.
- Не передавай весь store или необработанный API-ответ компоненту, которому нужен только небольшой набор полей.
- Не делай компонент одновременно страницей, API-клиентом и универсальным контейнером.
- Повторяемую логику выноси в composable только после появления реальной общей границы.
Маршрутный flow:
маршрут -> src/views/CatalogView.vue -> src/components/CatalogPage.vue
*View.vue только подключает page-компонент. Page-компонент собирает экран,
загружает его данные и управляет flow.
4. TypeScript и именование
PascalCase.vue— компоненты;camelCase.ts— TypeScript; composable —useName.ts; store —camelCase.ts.- Компоненты, типы и интерфейсы —
PascalCase; функции, переменные и props —camelCase; boolean — с префиксомis,has,canилиshould. - Типы protobuf импортируй из
src/api/generated/**и не дублируй вsrc/types. - Типы форм и редакторов храни в
src/typesлибо рядом с компонентом, если они не используются за его пределами. - Не используй
any; неизвестное внешнее значение должно бытьunknownс последующим сужением типа. - Не подавляй TypeScript через
asбез причины и не меняй API-контракт молча. - Импорты: внешние пакеты, затем
@/, затем относительные; неиспользуемые импорты запрещены. - Без точек с запятой, одинарные кавычки, Prettier с шириной 100.
5. API, асинхронность и ошибки
- API вызывается только через generated clients и существующие обёртки
(
getAuthClient()и аналоги). Не создавай ручные fetch/axios-вызовы для уже описанного endpoint. - Ошибку обрабатывай в том же flow. Пользовательские сообщения показывай через
useMessage()из Naive UI и пиши по-русски. - Загрузку держи в простом
ref(false)сtry/finally; повторную отправку блокируй, если она может испортить данные. - Поллинг делай через существующий
useSimplePolling. - Ошибка не должна теряться в
catch: покажи её или передай выше с контекстом.console.errorоставляй для диагностики после пользовательской обработки. - Пустое, загрузочное и ошибочное состояния должны быть явными в template.
- Не перезаписывай локальный черновик данными фонового обновления во время редактирования.
6. State и stores
- В store держи только состояние, которым пользуются разные маршруты или компоненты, и действия, изменяющие это состояние.
- Не превращай store в контейнер всех запросов приложения.
- Преобразуй API-ответ в форму для UI на границе store/page flow.
- Секреты и токены не выводи в UI и не помещай в URL.
- Локальный черновик держи в
ref/reactive; серверное состояние синхронизируй явно после сохранения.
7. Стили и дизайн
- Компонентные стили по умолчанию пишутся в
<style scoped>. - CSS-свойства располагаются в алфавитном порядке; это проверяет Stylelint.
- Новые цвета не хардкодь: используй палитру и CSS-переменные из
src/assets/base.css. Новый глобальный токен добавляй в:rootтолько при необходимости и с понятным семантическим именем. - Для экранного UI используй компоненты Naive UI (
n-*) и тему изApp.vue, а не стилизацию нативных<input>и<button>. - Иконки подключай из
@viconsчерезIconиз@vicons/utils; размер задавай черезsize. - Адаптивность проектируй mobile-first; desktop-брейкпоинт —
@media (width >= 1024px). - Сохраняй текущий flow:
HeaderMenuсверху,.center-block-customдля прокрутки,.width700для дел и.width1200для списков. - Бумажные листы используют
MessagePaperиfont_old_typer; UI-формы, меню и кнопки этот шрифт не используют. - Не добавляй глобальный utility-класс для одного компонента.
8. Формы, доступность и состояния
- Валидируй ввод до API-вызова и учитывай серверную ошибку; клиентская проверка не заменяет серверную.
- У обязательного поля есть понятная подпись и сообщение об ошибке.
- Во время загрузки показывай состояние и не допускай повторной отправки.
- Интерактивные элементы доступны с клавиатуры и имеют видимый focus. Не убирай outline без равноценной замены.
- Модальные окна просмотра делай через
n-modal+n-cardсrole="dialog"иaria-modal="true"; изображения имеют осмысленныйalt. - Hover не единственный способ понять состояние: учитывай focus, disabled, loading, success и error.
9. Тесты и проверки
- Для чистой функции с нетривиальной логикой добавь небольшой тест рядом с ней или используй существующий механизм тестирования.
- Не добавляй тесты для тривиального прокси или очевидной разметки; проверяй пользовательский flow, валидацию, преобразования и ошибки.
- Перед передачей фронтенд-задачи запускай:
npm run type-check
npm run lint
npm run lint:css
- После изменений сборки, маршрутов или generated API дополнительно запускай
npm run build; после измененияproto/main.protoсначала запускайnpm run proto:gen.
10. Допустимое отклонение
Отклонение от стандарта требует до изменения кода: явного разрешения пользователя, короткого письменного обоснования и указания затронутых файлов. Если существующий код противоречит стандарту, не исправляй его попутно: исправь только необходимый участок и отдельно предложи миграцию.