Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Как устроен App Router в Next.js
Основная концепция
App Router — это система маршрутизации в Next.js 13+, которая заменила Pages Router. Она использует файловую систему для определения маршрутов, где структура папок прямо соответствует URL структуре приложения. Каждая папка в директории app/ представляет собой segment (сегмент) маршрута.
Структура и базовые компоненты
В App Router используется несколько ключевых файлов:
- page.tsx — основной компонент, отображается при обращении к маршруту
- layout.tsx — оборачивает все дочерние страницы, сохраняет состояние
- error.tsx — обработчик ошибок для boundary
- loading.tsx — компонент для отображения на время загрузки
- not-found.tsx — кастомная 404 страница
- route.ts — API маршруты (GET, POST, PUT, DELETE)
Пример структуры
app/
├── layout.tsx # Root layout
├── page.tsx # /
├── about/
│ └── page.tsx # /about
├── products/
│ ├── layout.tsx # Layout для /products и подстраниц
│ ├── page.tsx # /products
│ └── [id]/
│ └── page.tsx # /products/123
└── api/
└── users/
└── route.ts # API endpoint POST /api/users
Динамические маршруты
Для создания динамических сегментов используются квадратные скобки:
// app/products/[id]/page.tsx
interface Props {
params: Promise<{ id: string }>; // Next.js 15+ требует Promise
}
export default async function ProductPage({ params }: Props) {
const { id } = await params;
return <div>Product: {id}</div>;
}
Catch-all маршруты
// app/docs/[[...slug]]/page.tsx
interface Props {
params: Promise<{ slug?: string[] }>;
}
export default async function DocsPage({ params }: Props) {
const { slug } = await params;
// /docs -> slug undefined
// /docs/guide/setup -> slug = ['guide', 'setup']
return <div>Docs: {slug?.join('/')}</div>;
}
Layouts — иерархия и переиспользование
Layout — компонент, который оборачивает дочерние page и сохраняет состояние при навигации:
// app/layout.tsx (Root Layout)
export const metadata = {
title: 'My App',
description: 'Generated by create next app',
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="ru">
<body>
<Header />
{children}
<Footer />
</body>
</html>
);
}
// app/dashboard/layout.tsx (Segment Layout)
export default function DashboardLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<div>
<Sidebar />
<main>{children}</main>
</div>
);
}
Server Components и Client Components
По умолчанию все компоненты в App Router — Server Components, что дает преимущества:
// app/products/page.tsx — Server Component по умолчанию
// Может напрямую обращаться к БД
export default async function ProductsPage() {
const products = await db.query('SELECT * FROM products');
return (
<div>
{products.map(p => (
<ProductCard key={p.id} product={p} />
))}
</div>
);
}
// app/components/filters.tsx — Client Component
'use client';
import { useState } from 'react';
export function Filters() {
const [filters, setFilters] = useState({});
return <div>...</div>;
}
Навигация и Link
Для навигации используется компонент Link из Next.js:
import Link from 'next/link';
export function Navigation() {
return (
<nav>
<Link href="/">Home</Link>
<Link href="/about">About</Link>
<Link href={`/products/${id}`}>Product</Link>
</nav>
);
}
Для программной навигации используется useRouter:
'use client';
import { useRouter } from 'next/navigation'; // ! navigation, не router
export function GoBack() {
const router = useRouter();
return (
<button onClick={() => router.push('/dashboard')}>
Вернуться
</button>
);
}
Преимущества App Router
- Server Components — автоматический SSR, безопасность, прямой доступ к БД
- Вложенные layouts — переиспользование UI, сохранение состояния
- Колокализация — файлы компонентов и стилей рядом
- Улучшенная обработка ошибок — error.tsx и not-found.tsx
- Loading states — loading.tsx для Suspense
- Параллельные маршруты — одновременная загрузка нескольких сегментов
Отличие от Pages Router
В старом Pages Router использовались _app.js и _document.js для глобальной конфигурации. В App Router это заменено на layout.tsx в корне app/ директории, что более гибко и понятно.