Навигация

Методы рендеринга в Next.js

Next.js поддерживает несколько стратегий рендеринга, и выбор между ними определяет баланс производительности, актуальности данных и SEO. Каждый метод решает конкретный сценарий: статические блоги, персонализированные дашборды, e-commerce с частыми обновлениями цен.

Общие концепции методов рендеринга без привязки к фреймворку разобраны в статье Методы рендеринга раздела «Производительность». Здесь — реализация в App Router: Route Segment Config, revalidatePath / revalidateTag, RSC, Streaming, Static Export и PPR. Кэширование в деталях — в статье Кэширование, Edge Runtime — в статье Edge Runtime.

Содержание

  1. CSR — Client-Side Rendering
  2. SSR — Server-Side Rendering
  3. SSG — Static Site Generation
  4. ISR — Incremental Static Regeneration
  5. RSC — React Server Components
  6. Streaming SSR
  7. Static Export
  8. PPR — Partial Prerendering
  9. Route Segment Config
  10. Связь с кэшированием и Edge
  11. Сравнительная таблица
  12. Рекомендации по выбору
  13. Ключевые тезисы для интервью
  14. Заключение
  15. Полезные ссылки

1. CSR — Client-Side Rendering (Рендеринг на клиенте)

Что это: Страница рендерится полностью в браузере с помощью JavaScript.

Когда использовать:

  • Дашборды, приватные страницы
  • Интерактивные приложения без SEO-требований
  • Контент, не требующий индексации

Как включить:

'use client'

import { useState, useEffect } from 'react'

export default function Page() {
  const [data, setData] = useState(null)

  useEffect(() => {
    fetch('/api/data')
      .then(res => res.json())
      .then(setData)
  }, [])

  return <div>{data?.title}</div>
}

Плюсы:

  • Мгновенная навигация после первой загрузки
  • Снижает нагрузку на сервер

Минусы:

  • Плохо для SEO (поисковики видят пустую страницу)
  • Долгая первая загрузка (FCP, LCP)
  • Зависимость от JavaScript

2. SSR — Server-Side Rendering (Рендеринг на сервере)

Что это: Страница генерируется на сервере при каждом запросе.

Когда использовать:

  • Динамический контент, который часто меняется
  • Персонализированные страницы
  • Данные, которые должны быть актуальными при каждом запросе

Как включить:

// app/page.tsx
export const dynamic = 'force-dynamic' // Отключает кэширование

export default async function Page() {
  const data = await fetch('https://api.example.com/data', {
    cache: 'no-store'
  })
  const posts = await data.json()

  return (
    <ul>
      {posts.map(post => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  )
}

Плюсы:

  • SEO-friendly (полный HTML при первом запросе)
  • Всегда актуальные данные
  • Быстрая первая отрисовка

Минусы:

  • Нагрузка на сервер при каждом запросе
  • Медленнее, чем статические страницы
  • Требует серверной инфраструктуры

3. SSG — Static Site Generation (Статическая генерация)

Что это: Страницы генерируются один раз при сборке проекта и раздаются как статические файлы.

Когда использовать:

  • Блоги, документация
  • Маркетинговые страницы
  • Контент, который редко меняется

Как включить:

// app/posts/page.tsx
export const dynamic = 'force-static' // По умолчанию в Next.js

export default async function Page() {
  const posts = await getAllPosts() // Выполняется при сборке

  return (
    <ul>
      {posts.map(post => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  )
}

Для динамических маршрутов:

// app/posts/[slug]/page.tsx
export async function generateStaticParams() {
  const posts = await fetch('https://api.example.com/posts').then(res => res.json())

  return posts.map(post => ({
    slug: post.slug
  }))
}

export default async function Page({ params }: { params: { slug: string } }) {
  const post = await getPost(params.slug)
  return <article>{post.content}</article>
}

Плюсы:

  • Максимальная производительность (CDN)
  • Отличное SEO
  • Низкая нагрузка на сервер

Минусы:

  • Долгая сборка при большом количестве страниц
  • Контент устаревает до следующей сборки

4. ISR — Incremental Static Regeneration (Инкрементальная статическая регенерация)

Что это: Комбинация SSG и SSR. Страницы генерируются статически, но могут обновляться в фоне с заданной периодичностью.

Когда использовать:

  • Контент, который обновляется периодически (раз в минуту/час/день)
  • Большие сайты, где полная пересборка нецелесообразна
  • E-commerce (цены, наличие товаров)

Как включить:

// app/page.tsx
export const revalidate = 60 // Обновлять каждые 60 секунд

export default async function Page() {
  const data = await fetch('https://api.example.com/data', {
    next: { revalidate: 60 } // Или через опции fetch
  })

  return <div>{data.title}</div>
}

On-Demand Revalidation (по запросу)

Механизм принудительного обновления кэша ISR-страницы по событию, без ожидания истечения revalidate.

Как это работает:

  1. Страница сгенерирована статически и лежит в кэше
  2. Происходит событие (например, добавлена новая статья в CMS)
  3. Вызывается API-эндпоинт, который очищает кэш для конкретной страницы
  4. Следующий запрос к странице генерирует её заново и кладёт свежую версию в кэш

revalidatePath — очищает кэш конкретного пути:

// app/api/revalidate/route.ts
import { revalidatePath } from 'next/cache'

export async function POST(request: Request) {
  const { path } = await request.json()

  revalidatePath(path) // Очищает кэш для /posts/my-article

  return Response.json({ revalidated: true, now: Date.now() })
}

revalidateTag — очищает кэш по тегу (более гибкий подход):

// app/posts/page.tsx
export default async function Page() {
  const posts = await fetch('https://api.example.com/posts', {
    next: { tags: ['posts'] } // Помечаем данные тегом
  }).then(res => res.json())

  return <PostsList posts={posts} />
}

// app/api/revalidate/route.ts
import { revalidateTag } from 'next/cache'

export async function POST(request: Request) {
  revalidateTag('posts') // Очищает ВСЕ данные с тегом 'posts'

  return Response.json({ revalidated: true })
}

Практический сценарий (CMS + Webhook):

// app/api/webhook/route.ts
import { revalidateTag } from 'next/cache'

export async function POST(request: Request) {
  const secret = request.headers.get('x-webhook-secret')

  if (secret !== process.env.WEBHOOK_SECRET) {
    return Response.json({ error: 'Invalid secret' }, { status: 401 })
  }

  const { event, data } = await request.json()

  if (event === 'post.updated') {
    revalidateTag(`post-${data.id}`) // Обновляем конкретный пост
    revalidatePath('/') // Обновляем главную (список постов)
  }

  return Response.json({ revalidated: true })
}

Разница между Path и Tag:

ПодходКогда использовать
revalidatePathЗнаешь точный URL страницы
revalidateTagНужно обновить несколько страниц/компонентов с одними данными

Пример с тегами:

// app/posts/page.tsx — использует тег 'posts'
const posts = await fetchPosts({ next: { tags: ['posts'] } })

// app/posts/[slug]/page.tsx — использует тег конкретного поста
const post = await fetchPost(slug, { next: { tags: [`post-${slug}`] } })

// app/api/revalidate/route.ts
revalidateTag('posts') // Обновит список постов
revalidateTag('post-my-article') // Обновит только конкретный пост

Поведение stale-while-revalidate:

  1. Запрос к странице с невалидным кэшем → stale-while-revalidate
  2. Пользователь получает старую версию (мгновенно)
  3. В фоне генерируется новая версия
  4. Следующие запросы получают свежую версию

Это означает, что первый пользователь после ревалидации может увидеть устаревший контент, но последующие — уже актуальный.

Плюсы:

  • Производительность SSG + актуальность SSR
  • Не нужна полная пересборка
  • Гибкость в выборе частоты обновления

Минусы:

  • Пользователь может увидеть устаревший контент (до revalidate)
  • Сложнее в настройке, чем чистый SSG

5. RSC — React Server Components (Серверные компоненты React)

Что это: Компоненты, которые выполняются исключительно на сервере. Не отправляются в бандл клиента.

Когда использовать:

  • Получение данных напрямую из компонента (БД, API, файловая система)
  • Работа с конфиденциальными данными (токены, ключи API, секреты)
  • Тяжёлые зависимости (не нужно тащить в клиентский бандл)
  • Статический контент (блоги, документация, маркетинговые страницы)
  • Компоненты, которые не требуют интерактивности (карточки, списки, навигация)
  • Композиция с клиентскими компонентами (обёртка для передачи данных)
  • Оптимизация размера бандла (большие библиотеки остаются на сервере)
  • Прямой доступ к backend-ресурсам без API-слоя
  • По умолчанию все компоненты в App Router — серверные

Как использовать:

// app/page.tsx — Серверный компонент (по умолчанию)
import { db } from '@/lib/db' // Прямой доступ к БД

export default async function Page() {
  const users = await db.user.findMany() // Async/await прямо в компоненте

  return (
    <ul>
      {users.map(user => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  )
}

Клиентский компонент (когда нужен интерактив):

// components/Counter.tsx
'use client'

import { useState } from 'react'

export function Counter() {
  const [count, setCount] = useState(0)

  return <button onClick={() => setCount(count + 1)}>{count}</button>
}

Плюсы:

  • Нулевой размер бандла для серверных компонентов
  • Прямой доступ к backend-ресурсам
  • Автоматический code splitting
  • Улучшенная безопасность (секреты не попадают в клиент)

Минусы:

  • Нет доступа к хукам (useState, useEffect)
  • Нет доступа к браузерным API (window, localStorage)
  • Нет обработчиков событий

Практическое соотношение RSC и Client Components:

В реальном Next.js проекте большинство компонентов — серверные (RSC). Типичное соотношение: 70-80% RSC, 20-30% Client Components.

Почему так:

  • Страницы, layouts, компоненты данных — всё это RSC по умолчанию
  • Клиентские компоненты нужны только там, где есть интерактивность (формы, модальные окна, слайдеры, дропдауны)
  • RSC лучше для производительности (меньше JavaScript на клиенте)

Типичные Client Components:

  • Формы с валидацией
  • Модальные окна, дропдауны, табы
  • Слайдеры, карусели
  • Компоненты с локальным состоянием (счётчики, toggle)
  • Обработчики событий (onClick, onChange, onSubmit)
  • Использование хуков (useState, useEffect, useRef)

Типичные RSC:

  • Страницы (app/**/page.tsx)
  • Layouts (app/**/layout.tsx)
  • Компоненты получения данных
  • Статические компоненты (карточки, списки, навигация)
  • Обёртки для клиентских компонентов

Терминология:

  • RSC — React Server Components (серверные компоненты)
  • Client Components — клиентские компоненты (официальное название)
  • Термин RCC (React Client Components) не используется официально, но иногда встречается в сообществах. Правильно говорить просто “Client Components” или “клиентские компоненты”.

6. Streaming SSR (Потоковый серверный рендеринг)

Что это: Разновидность SSR, где HTML отправляется частями (чанками) по мере готовности компонентов.

Когда использовать:

  • Страницы с тяжёлыми компонентами
  • Когда нужно показать контент как можно быстрее
  • Использование Suspense для graceful degradation

Как включить:

// app/page.tsx
import { Suspense } from 'react'

export default function Page() {
  return (
    <div>
      <h1>Главная страница</h1>

      <Suspense fallback={<div>Загрузка...</div>}>
        <SlowComponent /> {/* Рендерится позже */}
      </Suspense>
    </div>
  )
}

async function SlowComponent() {
  const data = await fetchSlowData() // Блокирующий запрос
  return <div>{data.content}</div>
}

Плюсы:

  • Быстрый Time to First Byte (TTFB)
  • Прогрессивная загрузка контента
  • Лучший пользовательский опыт

Минусы:

  • Сложнее в отладке
  • Требует поддержки Suspense на клиенте

7. Static Export (Статический экспорт)

Что это: Режим деплоя, при котором Next.js приложение экспортируется как полностью статический сайт (HTML, CSS, JS файлы), который можно хостить на любом статическом сервере без Node.js.

Отличие от SSG:

  • SSG — это стратегия рендеринга (страницы генерируются при сборке)
  • Static Export — это режим вывода (всё приложение становится статическим)
  • При Static Export ВСЕ страницы принудительно становятся SSG
  • SSG может работать в обычном Next.js с сервером, Static Export — нет

Когда использовать:

  • Хостинг без Node.js (GitHub Pages, Netlify static, Vercel static, S3)
  • Полностью статические сайты (блоги, документация, лендинги)
  • Когда не нужны динамические функции сервера
  • Снижение затрат на инфраструктуру

Как включить:

// next.config.js
module.exports = {
  output: 'export', // Включает статический экспорт
}

Что НЕ работает с Static Export:

  • SSR (серверный рендеринг)
  • ISR (инкрементальная регенерация)
  • API Routes (app/api/**)
  • Middleware
  • Image Optimization (через next/image default loader)
  • Dynamic Routes без generateStaticParams
  • Rewrites, redirects (на уровне сервера)
  • On-demand revalidation

Ограничения динамических маршрутов:

// app/posts/[slug]/page.tsx
// Обязательна generateStaticParams для всех динамических маршрутов
export async function generateStaticParams() {
  const posts = await fetch('https://api.example.com/posts').then(res => res.json())

  return posts.map(post => ({
    slug: post.slug
  }))
}

export default async function Page({ params }: { params: { slug: string } }) {
  // ...
}

Плюсы:

  • Можно хостить где угодно (любой статический хостинг)
  • Не нужен Node.js сервер
  • Минимальные затраты на инфраструктуру
  • Максимальная производительность (CDN)
  • Простой деплой

Минусы:

  • Нет динамических функций (SSR, ISR, API routes)
  • Все данные должны быть доступны при сборке
  • Долгая сборка при большом количестве страниц
  • Нет on-demand revalidation
  • Ограничения на динамические маршруты

Сравнение с SSG:

АспектSSGStatic Export
ТипСтратегия рендерингаРежим деплоя
СерверМожет бытьНе нужен
ISRПоддерживаетсяНе поддерживается
API RoutesПоддерживаютсяНе поддерживаются
ХостингNode.js сервер или CDNЛюбой статический хостинг

Практический пример:

// next.config.js
module.exports = {
  output: 'export',
  images: {
    unoptimized: true, // Отключает оптимизацию изображений
  },
}

Команда сборки:

npm run build
# Создаёт папку 'out' с статическими файлами

Подробнее про деплой и режимы вывода — в статье Деплой Next.js.


8. PPR — Partial Prerendering (Частичный пререндеринг)

Что это: Гибридная стратегия, при которой страница имеет статическую оболочку (генерируется при сборке) и динамические части (рендерятся на сервере при запросе). Использует Suspense для определения границ.

Когда использовать:

  • Страницы с общей статической структурой (header, footer, sidebar) и динамическим контентом
  • Персонализированные страницы (статический layout + динамические данные пользователя)
  • E-commerce (статическая карточка товара + динамические отзывы/цены)
  • Когда нужно избежать client-side fetching для динамических данных

Как включить:

// next.config.js
module.exports = {
  experimental: {
    ppr: true, // Глобальное включение PPR
  },
}
// app/page.tsx
import { Suspense } from 'react'

export const experimental_ppr = true // Или на уровне страницы

export default function Page() {
  return (
    <div>
      {/* Статическая оболочка — рендерится при сборке */}
      <header>Навигация</header>
      <main>
        <h1>Главная страница</h1>

        {/* Динамическая часть — рендерится на сервере при запросе */}
        <Suspense fallback={<div>Загрузка...</div>}>
          <UserProfile /> {/* Персонализированный контент */}
        </Suspense>

        {/* Ещё одна динамическая часть */}
        <Suspense fallback={<div>Загрузка...</div>}>
          <RecentPosts /> {/* Динамические данные */}
        </Suspense>
      </main>
      <footer>Футер</footer>
    </div>
  )
}

async function UserProfile() {
  const user = await fetchUser() // Выполняется при каждом запросе
  return <div>Привет, {user.name}!</div>
}

async function RecentPosts() {
  const posts = await fetchRecentPosts() // Выполняется при каждом запросе
  return <ul>{posts.map(post => <li key={post.id}>{post.title}</li>)}</ul>
}

Как это работает:

  1. При сборке генерируется статическая оболочка (header, footer, layout, всё вне Suspense)
  2. При запросе страницы сервер мгновенно отдаёт статическую оболочку
  3. Динамические части (внутри Suspense) рендерятся на сервере параллельно
  4. Клиент получает статический HTML сразу, динамические части подгружаются через streaming

Плюсы:

  • Мгновенная первая отрисовка (статическая оболочка кэшируется)
  • Динамический контент без client-side fetching
  • Лучший UX: пользователь видит контент сразу, динамические части загружаются прогрессивно
  • Меньше нагрузки на сервер, чем SSR (статическая часть не рендерится при каждом запросе)
  • Автоматическое определение статических/динамических границ через Suspense

Минусы:

  • Экспериментальная функция (может измениться в будущих версиях)
  • Требует явного использования Suspense для динамических частей
  • Сложнее в отладке, чем чистый SSG или SSR
  • Не все хостинги поддерживают PPR (нужен Node.js сервер)

Сравнение с другими методами:

АспектSSRSSGISRPPR
Статическая частьНетВся страницаВся страницаОболочка
Динамическая частьВся страницаНетНетТолько в Suspense
Рендеринг при запросеДаНетПериодическиТолько динамические части
ПроизводительностьСредняяМаксимальнаяМаксимальнаяВысокая
Актуальность данныхВсегдаПри сборкеПериодическиВсегда (для динамических частей)

Практический пример (e-commerce):

// app/product/[id]/page.tsx
import { Suspense } from 'react'

export const experimental_ppr = true

export default async function ProductPage({ params }: { params: { id: string } }) {
  // Статические данные (могут быть кэшированы)
  const product = await fetchProduct(params.id)

  return (
    <div>
      {/* Статическая часть */}
      <h1>{product.name}</h1>
      <img src={product.image} alt={product.name} />
      <p>{product.description}</p>

      {/* Динамическая часть (цена, наличие) */}
      <Suspense fallback={<div>Проверка наличия...</div>}>
        <ProductStock productId={params.id} />
      </Suspense>

      {/* Динамическая часть (отзывы) */}
      <Suspense fallback={<div>Загрузка отзывов...</div>}>
        <ProductReviews productId={params.id} />
      </Suspense>
    </div>
  )
}

async function ProductStock({ productId }: { productId: string }) {
  const stock = await fetchStock(productId) // Актуальные данные при каждом запросе
  return <div>В наличии: {stock.count} шт.</div>
}

async function ProductReviews({ productId }: { productId: string }) {
  const reviews = await fetchReviews(productId) // Актуальные отзывы
  return <ul>{reviews.map(review => <li key={review.id}>{review.text}</li>)}</ul>
}

Когда НЕ использовать PPR:

  • Полностью статические страницы (используй SSG)
  • Полностью динамические страницы (используй SSR)
  • Когда нужен полный контроль над кэшированием (используй ISR)
  • Когда хостинг не поддерживает Node.js (используй Static Export)

Route Segment Config

App Router позволяет управлять стратегией рендеринга через экспортируемые константы в page.tsx / layout.tsx.

ОпцияЗначениеЭффект
dynamic'force-static'Принудительный SSG
dynamic'force-dynamic'Принудительный SSR
revalidate60ISR с TTL 60 секунд
dynamicParamsfalse404 для маршрутов вне generateStaticParams
runtime'edge'Edge Runtime вместо Node.js

Пример гибридной настройки:

// app/blog/[slug]/page.tsx
export const revalidate = 3600 // Обновлять каждый час
export const dynamicParams = true // Новые посты генерируются лениво

export default async function Page({ params }: { params: { slug: string } }) {
  const post = await getPost(params.slug)
  return <article>{post.content}</article>
}

Связь с кэшированием и Edge

Стратегии рендеринга тесно связаны с кэшированием и runtime:

  • Кэширование. Четыре уровня кэша Next.js, unstable_cache, поведение Cache-Control для ISR-страниц — в статье Кэширование.
  • Edge Runtime. Middleware и часть страниц/Route Handlers можно выполнять на CDN-узлах вместо Node.js. Ограничения и конфигурация — в статье Edge Runtime.
  • Деплой. Какая платформа поддерживает ISR, PPR и Edge, режимы вывода export / standalone — в статье Деплой Next.js.
  • RSC Payload. Как сериализуются серверные компоненты — в статье RSC Payload.

Сравнительная таблица

МетодSEOСкоростьАктуальностьСложность
CSRПлохоМедленная FCPВсегдаНизкая
SSRОтличноБыстраяВсегдаСредняя
SSGОтличноМаксимальнаяПри сборкеНизкая
ISRОтличноМаксимальнаяПериодическиСредняя
RSCОтличноМаксимальнаяЗависит от данныхСредняя
StreamingОтличноБыстрая TTFBВсегдаВысокая
Static ExportОтличноМаксимальнаяПри сборкеНизкая
PPRОтличноВысокаяВсегда (динамические части)Высокая

Рекомендации по выбору

СценарийРекомендуемая стратегия
Блог, документация, лендингSSG или Static Export
Маркетинговый сайт с редкими обновлениямиSSG
E-commerceISR + RSC, PPR или SSR с кэшем
Новостной сайтISR или SSR
Личный кабинет, дашбордCSR + RSC
Персонализированные страницыSSR или PPR
Глобальное приложение с низкой задержкойSSG + CDN + Edge Functions
Админка внутри компанииCSR
GitHub Pages / статический хостингStatic Export
Страницы с общей структурой и динамическими даннымиPPR

Практический пример архитектуры

┌─────────────────────────────────────────────────────────────┐
│                         Пользователь                         │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
            ┌─────────────────┐
            │   Edge Function │  <- аутентификация, A/B, geo
            │   (middleware)  │
            └────────┬────────┘
                     │
                     ▼
            ┌─────────────────┐
            │       CDN       │  <- кэш статики и ISR
            └────────┬────────┘
                     │
        ┌────────────┼────────────┐
        │            │            │
        ▼            ▼            ▼
   ┌─────────┐  ┌─────────┐  ┌─────────┐
   │  SSG    │  │  ISR    │  │  SSR    │
   │  страницы│  │  страницы│  │  страницы│
   └─────────┘  └─────────┘  └─────────┘
        │            │            │
        └────────────┼────────────┘
                     │
                     ▼
            ┌─────────────────┐
            │   API / БД      │
            └─────────────────┘

Сноски

FCP (First Contentful Paint) — время до первого отображения любого контента на экране (текст, изображение, SVG). Показывает, насколько быстро пользователь видит, что страница “загружается”.

LCP (Largest Contentful Paint) — время до отрисовки самого крупного видимого элемента (обычно главное изображение или заголовок). Ключевая метрика воспринимаемой скорости загрузки.


Ключевые тезисы для интервью

  • CSR рендерит страницу в браузере (плохо для SEO, быстрая навигация после загрузки); SSR генерирует HTML на каждый запрос (SEO-friendly, нагрузка на сервер); SSG генерирует HTML при сборке (максимальная производительность, данные устаревают).
  • ISR = SSG + периодическое обновление через stale-while-revalidate: первый пользователь после истечения кэша видит старую версию, пока страница регенерируется. revalidatePath инвалидирует конкретный URL, revalidateTag — все данные с тегом.
  • RSC выполняются только на сервере и не попадают в JS-бандл клиента — хуки и браузерные API недоступны. В App Router ~70-80% компонентов серверные, клиентские нужны только для интерактивности.
  • Streaming SSR отправляет HTML чанками через Suspense, улучшая TTFB на страницах с тяжёлыми компонентами. Static Export — всё приложение статические файлы без Node.js; SSR, ISR и API Routes недоступны.
  • PPR объединяет подходы: статическая оболочка генерируется при сборке, динамические части рендерятся через Suspense при запросе — один файл содержит и мгновенно отдаваемый HTML, и живой контент.
  • Route Segment Config (dynamic, revalidate, runtime) управляет стратегией рендеринга на уровне файла; generateStaticParams перечисляет динамические маршруты для SSG.
  • On-demand revalidation (webhook из CMS) обновляет страницы по событию без ожидания TTL: revalidatePath для точного URL, revalidateTag для набора страниц.
  • Для большинства проектов оптимален гибридный подход: SSG/ISR для публичных страниц, RSC + Client Components для интерактивных разделов.

Заключение

Правильный выбор стратегии рендеринга — один из ключевых архитектурных решений в Next.js проекте. SSG и Static Export дают максимальную производительность для контента, не требующего актуальности. ISR добавляет периодическое обновление без полной пересборки. SSR гарантирует свежие данные при каждом запросе. RSC сокращают JS-бандл, вынося логику на сервер. PPR объединяет всё: статическая оболочка + динамические части по запросу. CDN и Edge Functions добавляют ещё один слой: кэширование и логика ближе к пользователю.

На собеседовании важно уметь объяснить не только концепцию каждого метода, но и его реализацию в App Router: dynamic, revalidate, generateStaticParams, revalidatePath / revalidateTag, разницу между SSG и Static Export и границы применения PPR.

Полезные ссылки