- Добавлена документация SLM-архитектуры, базовых правил и прикладных разделов - Добавлены разделы: стили, SVG-спрайты, шаблоны генерации, PostCSS, REST, Realtime - Удалены устаревшие файлы (спрайты, скрипты, стили из app/)
202 lines
8.6 KiB
Markdown
202 lines
8.6 KiB
Markdown
---
|
||
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` — модули, потому что это самостоятельные части страницы со своей структурой и публичной точкой входа.
|