Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Механизм генерации HTML в Next.js
Next.js использует несколько стратегий рендеринга для преобразования React компонентов в HTML. Понимание этих механизмов критично для оптимизации производительности и SEO.
SSR (Server-Side Rendering)
// app/page.tsx - по умолчанию SSR в Next.js 13+
import { db } from '@/lib/db';
export default async function HomePage() {
// Это асинхронный компонент - выполняется на сервере
const posts = await db.posts.findMany();
// HTML генерируется на сервере с реальными данными
return (
<main>
<h1>Посты</h1>
<ul>
{posts.map(post => (
<li key={post.id}>{post.title}</li>
))}
</ul>
</main>
);
}
// Процесс:
// 1. Запрос пришел на /
// 2. Next.js запустил HomePage компонент на сервере
// 3. Выполнил await db.posts.findMany()
// 4. React превратил JSX в HTML строку
// 5. Отправил HTML клиенту
// 6. Браузер отображает HTML
// 7. React hydrate интерактивности (если нужны)
SSG (Static Site Generation)
// app/posts/[slug]/page.tsx
export async function generateStaticParams() {
// Эта функция выполняется на билд-тайме
const posts = await db.posts.findMany();
// Возвращает все возможные параметры
return posts.map(post => ({
slug: post.slug
}));
}
export default async function PostPage({ params }) {
const post = await db.posts.findUnique({
where: { slug: params.slug }
});
return (
<article>
<h1>{post.title}</h1>
<p>{post.content}</p>
</article>
);
}
// Процесс на bildjoin time (npm run build):
// 1. Next.js вызывает generateStaticParams()
// 2. Получает список всех постов: [{slug: 'post-1'}, {slug: 'post-2'}...]
// 3. Для каждого slug генерирует папку: posts/post-1/page.html, posts/post-2/page.html
// 4. Сохраняет статический HTML
// 5. При запросе просто отдает готовый HTML
ISR (Incremental Static Regeneration)
// app/articles/[id]/page.tsx
export const revalidate = 60; // переиндексировать каждые 60 сек
export async function generateStaticParams() {
return [
{ id: '1' },
{ id: '2' },
{ id: '3' }
];
}
export default async function ArticlePage({ params }) {
const article = await db.articles.findUnique({
where: { id: params.id }
});
return (
<article>
<h1>{article.title}</h1>
<time>{article.updatedAt}</time>
</article>
);
}
// Процесс ISR:
// 1. Генерируются статические страницы при билде
// 2. При первом запросе после 60 сек отдается старая версия
// 3. В фоне на сервере регенерируется новая версия
// 4. Следующий запрос получит новую версию
// 5. Если вывести новый ID, Next.js создаст новую страницу on-demand
CSR (Client-Side Rendering)
// 'use client' - этот файл исполняется в браузере
'use client';
import { useState, useEffect } from 'react';
export default function InteractiveComponent() {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
// Этот код выполняется только в браузере
fetch('/api/v1/data')
.then(r => r.json())
.then(setData)
.finally(() => setLoading(false));
}, []);
if (loading) return <div>Загрузка...</div>;
return <div>{data.message}</div>;
}
// Процесс CSR:
// 1. Сервер отправляет HTML скелет (обычно с Suspense fallback)
// 2. Браузер загружает JavaScript бандл
// 3. React hydrate компонент
// 4. useEffect запускает fetch запрос
// 5. После получения данных компонент обновляется
Гибридный подход
// app/dashboard/page.tsx
import { Suspense } from 'react';
import StaticHeader from '@/components/StaticHeader'; // SSR
import UserPanel from '@/components/UserPanel'; // SSR
import RealtimeChart from '@/components/RealtimeChart'; // CSR
export default function Dashboard() {
return (
<main>
<StaticHeader /> {/* Отрендерится на сервере */}
<Suspense fallback={<div>Загрузка панели...</div>}>
<UserPanel /> {/* Может быть асинхронным */}
</Suspense>
<RealtimeChart /> {/* Клиентский компонент с real-time updates */}
</main>
);
}
// Процесс:
// 1. StaticHeader и UserPanel рендерятся на сервере
// 2. RealtimeChart отправляется как JS код
// 3. Браузер получает готовый HTML + hydrate реактивные части
Процесс на билд-тайме
build/
├── server/ # Серверный код для SSR
├── static/ # Статические страницы (SSG)
├── .next/
│ ├── cache/ # Кэш для ISR
│ └── routes-manifest.json
Процесс:
1. Парс app/ директории
2. Определение типа каждой страницы (SSR/SSG/ISR)
3. Вызов generateStaticParams() для SSG
4. Генерация HTML для статических страниц
5. Подготовка серверного кода для SSR
6. Создание бандла клиентского JavaScript
7. Создание manifest файлов для маршрутизации
Оптимизация
// Кэширование запросов
export default async function Page() {
// Автоматически кэшируется
const data = await fetch('/api/v1/data', {
next: { revalidate: 3600 } // кэш на 1 час
});
return <div>{data}</div>;
}
// Использование ISR для часто меняющихся данных
export const revalidate = 10; // обновлять каждые 10 сек
// Ленивая загрузка для тяжелых компонентов
const HeavyComponent = dynamic(() => import('@/components/Heavy'), {
ssr: false, // не генерировать на сервере
loading: () => <div>Загрузка...</div>
});
Выбор стратегии зависит от типа данных: статические данные -> SSG, динамические -> SSR, реал-тайм -> CSR.