- Добавлена документация SLM-архитектуры, базовых правил и прикладных разделов - Добавлены разделы: стили, SVG-спрайты, шаблоны генерации, PostCSS, REST, Realtime - Удалены устаревшие файлы (спрайты, скрипты, стили из app/)
187 lines
8.2 KiB
Markdown
187 lines
8.2 KiB
Markdown
---
|
||
title: Файлы роутинга
|
||
description: Как работать со страницами и другими файлами роутинга Next.js App Router.
|
||
---
|
||
|
||
# Файлы роутинга
|
||
|
||
Как работать со страницами и другими файлами роутинга Next.js App Router.
|
||
|
||
## Назначение
|
||
|
||
`src/app/**` — точка входа приложения и слой файлового роутинга Next.js.
|
||
|
||
Файлы роутинга не реализуют интерфейс. Они описывают маршрут: читают параметры, получают данные первого рендера, подготавливают кеш или состояние и передают результат в screen.
|
||
|
||
Границы слоя описаны в [Архитектура → Слои → App](../basics/architecture/reference/layers.md#слой-app).
|
||
|
||
## Граница ответственности
|
||
|
||
| Область | Где живёт |
|
||
|---|---|
|
||
| Файлы маршрутов (`page.tsx`, `layout.tsx`, `loading.tsx`, `error.tsx`, `not-found.tsx`) | `src/app/**` |
|
||
| Параметры маршрута, `metadata`, `redirect()`, `notFound()` | `src/app/**` |
|
||
| Серверные запросы для первого рендера | `src/app/**`, через готовые клиенты и сервисы нижних слоёв |
|
||
| Прогрев SWR-кеша, начальное состояние, подключение провайдеров | `src/app/**`, только через готовые обёртки из нижних слоёв |
|
||
| UI страницы | `screens/` |
|
||
| Каркас страницы: header, footer, sidebar | `layouts/` |
|
||
| Провайдеры, сторы, хуки, API-клиенты, сервисы | нижние слои (`screens/`, `business/`, `infrastructure/`, `shared/`) |
|
||
| CSS Modules и стили компонентов | рядом с компонентами, не в `src/app/**` |
|
||
|
||
## Что можно делать в `page.tsx`
|
||
|
||
- Экспортировать `metadata` или `generateMetadata`.
|
||
- Читать `params` и `searchParams`.
|
||
- Нормализовать и валидировать параметры маршрута.
|
||
- Делать серверные запросы для первого рендера через готовые клиенты или сервисы.
|
||
- Вызывать `redirect()` и `notFound()`.
|
||
- Готовить начальные данные для screen.
|
||
- Готовить SWR `fallback` и передавать его в готовый провайдер.
|
||
- Подключать готовый провайдер стора страницы и передавать начальное состояние.
|
||
- Рендерить screen или композицию из готовых обёрток и screen.
|
||
|
||
## Что запрещено
|
||
|
||
- Писать UI-разметку страницы прямо в файле роутинга.
|
||
- Создавать локальные компоненты внутри `src/app/**`.
|
||
- Добавлять CSS Modules, стили компонентов, `components/`, `styles/`, `hooks/`, `stores/`, `services/` внутри `src/app/**`.
|
||
- Реализовывать провайдеры, сторы, хуки, API-клиенты или сервисы в файлах роутинга.
|
||
- Размещать бизнес-логику, мапперы и правила предметной области в файлах роутинга.
|
||
- Вызывать `useSWR` и доменные клиентские хуки в файлах роутинга.
|
||
|
||
## Страницы
|
||
|
||
Страница объявляется через `export default function`. Для серверных запросов используется `async function`.
|
||
|
||
```tsx
|
||
import type { Metadata } from 'next'
|
||
import { ProfileScreen } from 'screens/profile'
|
||
|
||
export const metadata: Metadata = {
|
||
title: 'Профиль',
|
||
description: 'Страница профиля пользователя',
|
||
}
|
||
|
||
type ProfilePageProps = {
|
||
params: Promise<{ id: string }>
|
||
}
|
||
|
||
export default async function ProfilePage({ params }: ProfilePageProps) {
|
||
const { id } = await params
|
||
|
||
return <ProfileScreen id={id} />
|
||
}
|
||
```
|
||
|
||
## Данные первого рендера
|
||
|
||
Если данные нужны до первого рендера, `page.tsx` получает их на сервере и передаёт в screen. Сам запрос выполняется через готовый клиент или сервис нижнего слоя.
|
||
|
||
```tsx
|
||
import { notFound } from 'next/navigation'
|
||
import { userApi } from 'infrastructure/backend-api'
|
||
import { UserScreen } from 'screens/user'
|
||
|
||
type UserPageProps = {
|
||
params: Promise<{ id: string }>
|
||
}
|
||
|
||
export default async function UserPage({ params }: UserPageProps) {
|
||
const { id } = await params
|
||
const user = await userApi.users.get(id)
|
||
|
||
if (!user) {
|
||
notFound()
|
||
}
|
||
|
||
return <UserScreen user={user} />
|
||
}
|
||
```
|
||
|
||
Если данные нужны нескольким клиентским SWR-хукам, файл роутинга может обернуть дерево в `SWRConfig` и передать `fallback`. Запросы стартуют на сервере, а клиентские хуки получают данные из кеша.
|
||
|
||
Ключи `fallback` должны совпадать с ключами внутри GET-хуков REST-клиента. Для array-key используется `unstable_serialize`.
|
||
|
||
```tsx
|
||
import type { ReactNode } from 'react'
|
||
import { SWRConfig, unstable_serialize } from 'swr'
|
||
import {
|
||
backendApi,
|
||
getCurrentUserKey,
|
||
getPostListKey,
|
||
} from 'infrastructure/backend-api'
|
||
|
||
type FeedLayoutProps = {
|
||
children: ReactNode
|
||
}
|
||
|
||
export default async function FeedLayout({ children }: FeedLayoutProps) {
|
||
const userPromise = backendApi.user.getCurrent()
|
||
const postsPromise = backendApi.posts.list()
|
||
|
||
return (
|
||
<SWRConfig
|
||
value={{
|
||
fallback: {
|
||
[unstable_serialize(getCurrentUserKey())]: userPromise,
|
||
[unstable_serialize(getPostListKey())]: postsPromise,
|
||
},
|
||
}}
|
||
>
|
||
{children}
|
||
</SWRConfig>
|
||
)
|
||
}
|
||
```
|
||
|
||
Подробнее о стратегиях запросов и начальных данных для клиентских хуков: [REST → Стратегии получения данных](../data/rest/strategies/index.md), [REST → Начальные данные для клиентских хуков](../data/rest/strategies/client-hooks-initial-data.md).
|
||
|
||
## Инициализация состояния
|
||
|
||
Файл роутинга может подключить готовый провайдер стора страницы, если состояние зависит от маршрута или данных первого рендера. Реализация стора и провайдера не размещается в `src/app/**`.
|
||
|
||
```tsx
|
||
import { ProfileScreen, ProfileStoreProvider } from 'screens/profile'
|
||
|
||
type ProfilePageProps = {
|
||
params: Promise<{ id: string }>
|
||
}
|
||
|
||
export default async function ProfilePage({ params }: ProfilePageProps) {
|
||
const { id } = await params
|
||
|
||
return (
|
||
<ProfileStoreProvider initialState={{ userId: id }}>
|
||
<ProfileScreen />
|
||
</ProfileStoreProvider>
|
||
)
|
||
}
|
||
```
|
||
|
||
## Layout
|
||
|
||
`layout.tsx` подключает готовую инициализацию приложения: глобальные стили, провайдеры и верхнеуровневые обёртки из нижних слоёв.
|
||
|
||
Вёрстка layout-каркаса выносится в слой `layouts/`. Реализация провайдеров, стилей и UI не размещается в `app/`.
|
||
|
||
## Error и Not Found
|
||
|
||
`error.tsx` и `not-found.tsx` делегируют разметку готовым screen или widget. В файле роутинга остаётся только адаптация API Next.js к пропсам нижнего слоя.
|
||
|
||
```tsx
|
||
'use client'
|
||
|
||
import { ErrorScreen } from 'screens/error'
|
||
|
||
type ErrorPageProps = {
|
||
error: Error & { digest?: string }
|
||
reset: () => void
|
||
}
|
||
|
||
const ErrorPage = ({ error, reset }: ErrorPageProps) => {
|
||
return <ErrorScreen error={error} reset={reset} />
|
||
}
|
||
|
||
export default ErrorPage
|
||
```
|