Files
nextjs-template/ai/nextjs-style-guide/applied/component.md
S.Gromov f2358da397 docs: добавить стайлгайд nextjs-style-guide в репозиторий
- Добавлена документация SLM-архитектуры, базовых правил и прикладных разделов
- Добавлены разделы: стили, SVG-спрайты, шаблоны генерации, PostCSS, REST, Realtime
- Удалены устаревшие файлы (спрайты, скрипты, стили из app/)
2026-04-30 19:32:10 +03:00

202 lines
8.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
title: Компонент
description: Как создавать React-компоненты внутри SLM-модулей.
---
# Компонент
Как создавать React-компоненты внутри SLM-модулей.
## Назначение
Компонент — минимальная UI-единица проекта. Это один `.tsx` файл без собственной папки, сегментов и публичного API.
Компонент может использовать стили, типы, хуки и другие ресурсы родительского модуля. Наличие связанных файлов в `styles/` или `types/` не превращает компонент в модуль.
## Компонент или модуль
Классификация определяется границей владения:
- `component` — один `.tsx` файл внутри модуля;
- `module` — папка с `index.ts`, сегментами и собственной публичной границей.
```text
user/
├── ui/
│ └── user-avatar.tsx # компонент
├── styles/
│ └── user-avatar.module.css # ресурс родительского модуля
├── types/
│ └── user-avatar.type.ts # ресурс родительского модуля
└── user.tsx # корневой компонент модуля
```
`user-avatar.tsx` остаётся компонентом, потому что у него нет собственной папки, `index.ts` и сегментов.
## Где размещать
Компонент размещается внутри модуля:
- В корне модуля, если это главная UI-сущность модуля.
- В `ui/`, если это вспомогательный компонент модуля.
```text
user/
├── ui/
│ └── user-avatar.tsx
├── styles/
│ ├── user.module.css
│ └── user-avatar.module.css
├── types/
│ ├── user.type.ts
│ └── user-avatar.type.ts
├── user.tsx
└── index.ts
```
`user.tsx` — корневой компонент модуля. `ui/user-avatar.tsx` — вспомогательный компонент этого же модуля.
## Что запрещено
- Заворачивать компонент в папку: `ui/header/header.tsx`.
- Создавать для компонента отдельный `index.ts`.
- Создавать собственные сегменты внутри папки компонента: `ui/header/styles/`, `ui/header/types/`, `ui/header/hooks/` и т.п.
- Декларировать внутри `.tsx` сторы, сервисы, API-клиенты, мапперы или утилиты. Для этого есть сегменты родительского модуля.
- Размещать бизнес-правила прямо в компоненте. Компонент может использовать готовые зависимости модуля, но не определяет их.
- Размещать компонент в `parts/` напрямую. `parts/` содержит только модули.
**Плохо**
```text
user/
└── ui/
└── user-avatar/
├── styles/
│ └── user-avatar.module.css
├── user-avatar.tsx
└── index.ts
```
**Хорошо**
```text
user/
├── ui/
│ └── user-avatar.tsx
├── styles/
│ └── user-avatar.module.css
└── types/
└── user-avatar.type.ts
```
## Стили и типы
Компонент использует ресурсы родительского модуля.
`styles/` и `types/` рядом с корневым компонентом — это сегменты модуля, а не собственные папки `.tsx` файла.
- CSS Module компонента лежит в `styles/` родительского модуля и называется по компоненту: `user-avatar.module.css`.
- Если у компонента есть CSS Module, его корневой класс всегда называется `.root`.
- Типы компонента лежат в `types/` родительского модуля и называются по компоненту: `user-avatar.type.ts`.
- Локальный `type Props` внутри `.tsx` не используется. Типы пропсов всегда выносятся в `types/` родительского модуля.
- Экспорт типа из `types/` не делает его публичным API. Публичным он становится только при реэкспорте из `index.ts` модуля.
Причина `.root`: в DevTools проще находить корневой DOM-узел компонента и отличать его от внутренних элементов.
## Реализация
- Компоненты объявляются через `const`.
- `React.FC` не используется.
- JSDoc-комментарий обязателен для компонента.
- Пропсы деструктурируются в теле компонента.
- `className` объединяется с `styles.root` через `cl()`.
- Побочные эффекты и состояние выносятся в хуки модуля, если перестают быть тривиальными.
- Компонент возвращает JSX и не содержит orchestration-код страницы или бизнес-домена.
`user/types/user-avatar.type.ts`
```ts
import type { ImageProps } from 'next/image'
/**
* Параметры UserAvatar.
*/
export type UserAvatarParams = {}
/** Пропсы базового изображения. */
type RootAttrs = ImageProps
export type UserAvatarProps = RootAttrs & UserAvatarParams
```
`user/ui/user-avatar.tsx`
```tsx
import cl from 'clsx'
import Image from 'next/image'
import type { UserAvatarProps } from '../types/user-avatar.type'
import styles from '../styles/user-avatar.module.css'
/**
* Аватар пользователя.
*
* Используется для:
* - отображения пользователя в карточке
* - отображения пользователя в шапке профиля
*/
export const UserAvatar = (props: UserAvatarProps) => {
const { className, ...imageProps } = props
return <Image {...imageProps} className={cl(styles.root, className)} />
}
```
`user/styles/user-avatar.module.css`
```css
.root {
display: block;
border-radius: 50%;
}
```
## Когда нужен модуль
Решение о выделении модуля остаётся за разработчиком. Поднимать компонент в модуль стоит, если он становится самостоятельной областью:
- получает свои вложенные компоненты;
- получает свои хуки, стор или сервисы;
- получает внутренние мапперы или утилиты;
- требует собственного публичного API;
- начинает переиспользоваться вне родительского модуля;
- становится отдельной зоной параллельной разработки.
Пример: страница — это screen-модуль, а самостоятельные секции страницы — вложенные модули в `parts/`.
```text
screens/home/
├── parts/
│ ├── hero-section/
│ │ ├── styles/
│ │ │ └── hero-section.module.css
│ │ ├── types/
│ │ │ └── hero-section.type.ts
│ │ ├── hero-section.tsx
│ │ └── index.ts
│ └── features-section/
│ ├── styles/
│ │ └── features-section.module.css
│ ├── types/
│ │ └── features-section.type.ts
│ ├── features-section.tsx
│ └── index.ts
├── styles/
│ └── home.module.css
├── types/
│ └── home.type.ts
├── home.screen.tsx
└── index.ts
```
`hero-section` и `features-section` — модули, потому что это самостоятельные части страницы со своей структурой и публичной точкой входа.