App Router формирует модель маршрутизации, основанную на файловой
структуре и композиции серверных и клиентских компонентов. Основная идея
заключается в том, что каждая директория внутри app
представляет сегмент маршрута, а файлы внутри неё определяют поведение
рендера, загрузки данных и обработки состояний.
В основе лежит сегментирование пути. Каждый каталог — это логическая часть URL, а вложенность определяет иерархию интерфейса.
app/
├─ layout.js
├─ page.js
├─ dashboard/
│ ├─ layout.js
│ ├─ page.js
│ └─ settings/
│ └─ page.js
layout.js задаёт общий каркас сегмента и сохраняется
между переходами, а page.js определяет содержимое
конкретного маршрута.
Ключевая особенность — сохранение состояния layout при навигации внутри вложенных сегментов. Это уменьшает количество перерисовок и повышает предсказуемость UI.
По умолчанию компоненты внутри App Router являются серверными. Это означает, что их выполнение происходит на сервере, а клиент получает уже готовый результат рендеринга.
Серверные компоненты позволяют:
Пример серверного компонента:
export default async function Page() {
const data = await fetch('https://api.example.com/items').then(r => r.json());
return (
<div>
{data.map(item => (
<div key={item.id}>{item.title}</div>
))}
</div>
);
}
Отсутствие состояния и эффектов в серверных компонентах формирует строгую модель детерминированного рендера.
Любой компонент, который требует интерактивности, явно помечается директивой:
"use client";
После этого компонент становится частью клиентского бандла и подлежит гидратации в браузере.
Типичные случаи использования клиентских компонентов:
Граница между серверными и клиентскими компонентами является ключевым архитектурным ограничением. Импорт клиентского компонента в серверный автоматически расширяет клиентский граф зависимостей.
App Router позволяет комбинировать оба типа компонентов в одном дереве:
Пример:
// Server Component
import Counter from './Counter';
export default async function Page() {
const data = await getData();
return (
<div>
<h1>{data.title}</h1>
<Counter />
</div>
);
}
// Client Component
"use client";
import { useState } from 'react';
export default function Counter() {
const [count, setCount] = useState(0);
return (
<button onCl ick={() => setCount(count + 1)}>
{count}
</button>
);
}
Такое разделение позволяет изолировать интерактивность и минимизировать клиентскую нагрузку.
Одним из ключевых механизмов App Router является потоковая передача HTML (streaming). Вместо ожидания полного построения страницы сервер отправляет готовые части UI по мере их готовности.
Это реализуется через Suspense:
import { Suspense } from 'react';
export default function Page() {
return (
<>
<Header />
<Suspense fallback={<Loading />}>
<SlowComponent />
</Suspense>
</>
);
}
Преимущества:
layout.js сохраняет состояние между навигациями внутри
сегмента. Это отличает App Router от классической модели полной
перерисовки страниц.
Характерные свойства:
Пример:
export default function DashboardLayout({ children }) {
return (
<div className="dashboard">
<Sidebar />
<main>{children}</main>
</div>
);
}
Каждый маршрут может иметь специализированные файлы управления состояниями загрузки и ошибок:
loading.js — отображается во время ожидания данныхerror.js — обрабатывает ошибки сегмента// loading.js
export default function Loading() {
return <div>Загрузка...</div>;
}
// error.js
"use client";
export default function Error({ error }) {
return <div>Ошибка: {error.message}</div>;
}
Эти механизмы интегрированы в систему Suspense и позволяют локализовать состояния UI.
В серверных компонентах fetch расширен механизмом
кэширования:
cache и
next.revalidateawait fetch('https://api.example.com/data', {
next: { revalidate: 60 }
});
Такой подход объединяет SSR и SSG в единую модель.
Server Actions позволяют выполнять серверные операции без явного API слоя.
"use server";
export async function createItem(formData) {
const title = formData.get('title');
await db.insert({ title });
}
Использование в форме:
<form action={createItem}>
<input name="title" />
<button type="submit">Создать</button>
</form>
Особенности:
App Router поддерживает сложные схемы навигации:
Позволяют отображать несколько независимых сегментов одновременно:
app/
├─ dashboard/
│ ├─ @analytics/
│ ├─ @team/
│ └─ page.js
Позволяют перехватывать переходы и отображать модальные окна без выхода из контекста страницы.
Архитектура App Router требует контроля границ между сервером и клиентом:
"use client"Чем больше логики остаётся в серверных компонентах, тем меньше стоимость гидратации и выше производительность.
Процесс рендера включает несколько этапов:
Эта модель позволяет совмещать серверную генерацию и клиентскую интерактивность без жёсткого разделения между SSR и SPA подходами.