Загрузка...
Загрузка...
Продолжая работу с платформой, вы принимаете условия Политики конфиденциальности и использование файлов cookie.
Streaming позволяет отправлять HTML клиенту по частям, по мере готовности. Вместо того чтобы ждать завершения всех запросов, пользователь сразу видит часть страницы, а остальное подгружается.
Без streaming весь HTML генерируется целиком до отправки:
Запрос к БД на список задач, запрос к API за статистикой, запрос на профиль. Все последовательно или параллельно, но HTML отправляется только когда все готово.
Белый экран, пока сервер не соберет всю страницу. TTFB может быть несколько секунд.
С streaming Next.js отправляет части страницы по мере их готовности:
HTML с layout, навигацией и скелетонами отправляется сразу.
Данные подгружаются по мере готовности. Каждый блок заменяет свой скелетон.
Самый простой способ добавить loading UI. Next.js автоматически оборачивает page.tsx в React Suspense с fallback'ом из loading.tsx:
// app/problems/loading.tsx
export default function Loading() {
return (
<div className="space-y-4">
{[...Array(5)].map((_, i) => (
<div
key={i}
className="h-16 bg-neutral-200 animate-pulse rounded-lg"
/>
))}
</div>
)
}
Пользователь видит скелетон сразу, а контент появляется по мере загрузки данных.
Для более точного контроля используй Suspense напрямую:
// app/dashboard/page.tsx
import { Suspense } from 'react'
export default function DashboardPage() {
return (
<div>
<h1>Дашборд</h1>
<Suspense fallback={<StatsSkeleton />}>
<UserStats />
</Suspense>
<Suspense fallback={<ActivitySkeleton />}>
<RecentActivity />
</Suspense>
<Suspense fallback={<ProgressSkeleton />}>
<RoadmapProgress />
</Suspense>
</div>
)
}
Каждый Suspense boundary стримится независимо. UserStats может появиться раньше RecentActivity.
// 1. Сервер отправляет HTML с fallback'ами
<div>
<h1>Дашборд</h1>
<div id="stats"><!-- StatsSkeleton --></div>
<div id="activity"><!-- ActivitySkeleton --></div>
</div>
// 2. Когда данные готовы, сервер отправляет <script>
// который заменяет fallback на реальный контент
<script>
$RC("stats", "<div>Решено задач: 42</div>")
</script>
Браузер получает поток данных по одному HTTP-соединению.
Suspense работает с async серверными компонентами:
// Этот компонент будет стримиться
async function UserStats() {
const stats = await db.user.getStats(userId)
// Запрос может занять 2 секунды
// Пользователь увидит скелетон, пока данные загружаются
return (
<div>
<p>Решено задач: {stats.problemsSolved}</p>
<p>Прочитано статей: {stats.docsRead}</p>
</div>
)
}
| loading.tsx | Suspense | |
|---|---|---|
| Область | Вся страница (page.tsx) | Конкретный компонент |
| Гранулярность | Один fallback на страницу | Несколько независимых |
| Настройка | Автоматическая | Ручная |
Практический совет:
Используй loading.tsx для простых страниц с одним блоком данных. Для дашбордов и сложных страниц используй Suspense для каждого блока отдельно, чтобы контент появлялся по мере готовности.