Стратегии рендеринга и получение данных

Разбираемся, когда строится HTML страницы и как опции fetch выбирают стратегию рендеринга.

В Next.js один и тот же React-код можно отдавать тремя способами: SSG (HTML при сборке), SSR (на каждый запрос) и ISR (пересборка по таймеру); а сам выбор делается опциями fetch.

Три стратегии

СтратегияКогда строится HTMLПодходит для
SSG (Static)Один раз при buildБлог, документация, лендинги
SSR (Dynamic)На каждый запросЛента, персональная страница, корзина
ISR (Incremental)При сборке + пересборка по таймеруКаталог товаров, новости

SSG — самый быстрый вариант: статика создаётся один раз и раздаётся (можно через CDN). SSR нужен, когда контент персональный или обязан быть всегда свежим. ISR — компромисс: страница статическая, но «протухает» через интервал и тихо пересобирается в фоне.

Данные грузят обычным fetch

В App Router не нужны useEffect и состояние загрузки. Серверный компонент — async, и вы просто ждёте данные:

// app/products/page.tsx — серверный компонент
export default async function ProductsPage() {
  const res = await fetch("https://api.example.com/products");
  const products = await res.json();

  return (
    <ul>
      {products.map((p) => (
        <li key={p.id}>{p.name}</li>
      ))}
    </ul>
  );
}

Запрос уходит с сервера, в браузер едет уже готовый HTML со списком.

Опции кэша = стратегия

Ключевая идея: в App Router стратегию рендеринга выбирают не отдельными функциями, а опциями fetch:

Опции fetchПоведениеЭффект
{ cache: "force-cache" }КэшироватьSSG (статика)
{ next: { revalidate: 60 } }Обновлять не чаще раза в 60 сISR
{ cache: "no-store" }Грузить каждый разSSR (динамика)
// всегда свежие данные → страница станет динамической (SSR)
const res = await fetch(url, { cache: "no-store" });

// ревалидация раз в минуту → ISR
const res2 = await fetch(url, { next: { revalidate: 60 } });

Дедупликация запросов

Если несколько компонентов делают одинаковый fetch с одним URL, Next.js за один рендер выполнит запрос один раз и переиспользует результат — данные можно грузить там, где они нужны, не пробрасывая пропсами. Смоделируем идею на чистом JS:

const cache = new Map();
function cachedFetch(url) {
  if (cache.has(url)) {
    console.log("из кэша:", url);
    return cache.get(url);
  }
  console.log("запрос:", url);
  const data = { url, ok: true };
  cache.set(url, data);
  return data;
}

cachedFetch("/api/user");
cachedFetch("/api/user");
cachedFetch("/api/posts");

Вывод:

запрос: /api/user
из кэша: /api/user
запрос: /api/posts

Итог

  • SSG — HTML на сборке (быстро); SSR — на каждый запрос; ISR — статика с фоновой пересборкой.
  • Данные грузят обычным fetch в async-серверном компоненте, без useEffect.
  • Опции cache/revalidate у fetch и задают стратегию; одинаковые запросы дедуплицируются.
Проверьте себя
1. Когда формируется HTML при стратегии SSG?
AНа каждый запрос пользователя
BОдин раз во время сборки (build)
CКаждые 60 секунд
DТолько в браузере
2. Чем ISR отличается от чистого SSG?
AISR рендерит на каждый запрос
BISR — статика, которая периодически пересобирается в фоне по таймеру revalidate
CISR работает только в Pages Router
DISR отключает кэширование полностью
3. Как грузят данные в серверном компоненте App Router?
AЧерез useEffect и useState
BОбычным fetch внутри async-компонента, с await
CТолько через getServerSideProps
DЧерез axios на клиенте
4. Какая опция fetch делает страницу динамической (SSR) — без кэша?
A{ next: { revalidate: 60 } }
B{ cache: 'force-cache' }
C{ cache: 'no-store' }
D{ static: true }
Поддержать проект