Files
evening_detective_frontend/docs/FRONTEND_CODE_STANDARD.md
2026-10-14 23:39:17 +07:00

13 KiB
Raw Permalink Blame History

Стандарт написания фронтенда

Статус: обязательный стандарт.

Этот документ — единственный источник правил для нового и изменяемого кода в 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. В стиле сайта font_old_typer используется также для текста, меню, кнопок и форм согласно docs/DESIGN_SYSTEM.md (выбор владельца от 14.10.2026). Игровые компоненты сохраняют существующую типографику. Рабочие экраны мигрируют отдельно.
  • Не добавляй глобальный 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. Допустимое отклонение

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