Next.js: Серверный рендеринг vs Статическая генерация

alexei07/07/2026 - 08:51
Next.js: Серверный рендеринг vs Статическая генерация

Next.js — это фреймворк на базе React, который поддерживает пререндеринг. Вместо того чтобы заставлять браузер рендерить всё с нуля, Next.js может предоставлять уже готовый HTML-код двумя разными способами.

1. Server-side Rendering (SSR) — Рендеринг на стороне сервера

При использовании SSR Next.js пререндерит страницу в HTML на сервере при каждом запросе пользователя.

  • Плюс: Данные всегда будут актуальными.
  • Минус: Время до первого байта (TTFB, Time to First Byte) будет выше, так как серверу нужно время на генерацию страницы.

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

При статической генерации Next.js пререндерит страницу в HTML заранее — например, на этапе сборки (build time). Полученный HTML-код можно глобально закэшировать с помощью CDN и отдавать пользователю мгновенно.

  • Плюс: Максимальная производительность.
  • Минус: Данные могут устареть к моменту запроса (если они изменились в базе данных после сборки).

Как оптимизировать работу со статикой?

Чтобы не жертвовать преимуществами SSR, используя только SSG, в Next.js есть два мощных инструмента:

  1. Incremental Static Generation (ISR): позволяет добавлять и обновлять страницы постепенно уже после этапа сборки.
  2. Client-side Fetching: статическая генерация «пустой» оболочки страницы с последующей загрузкой данных на стороне клиента.

Практический пример: Приложение электронной коммерции

Рассмотрим типичное приложение. Каждая страница требует своего подхода к работе с данными:

  • О нас (About Us): Данные статичны (текст про компанию), запрашивать их из БД не нужно.
    • Стратегия:Static Generation без данных.
  • Все товары / Каждый товар: Список товаров и страницы конкретных товаров загружаются из базы. Для всех пользователей они выгляд'т одинаково.
    • Стратегия:Static Generation с данными + Incremental Static Generation (ISR).
  • Корзина (Shopping Cart): Содержимое корзины уникально для каждого пользователя.
    • Стратегия:Static Generation без данных + Client-side Fetching.

Настройка пререндеринга для каждой страницы

1. Страница «О нас» (Static Generation without data)

Если странице не нужны внешние данные, она автоматически пререндерится в HTML при сборке. Это поведение по умолчанию.


// pages/about.js
export default function About() {
  return (
    <div>
      <h1>О нас</h1>
      {/* Контент страницы */}
    </div>
  )
}

2. Страница «Все товары» (Static Generation with Data)

Для загрузки данных из БД на этапе сборки используется функция getStaticProps. Она выполняется в среде Node.js, поэтому код внутри неё не попадает в клиентский JS-бандл — вы можете безопасно делать запросы к базе данных напрямую.


// Функция запускается на сервере во время сборки
export async function getStaticProps() {
  return {
    props: {
      products: await getProductsFromDatabase()
    }
  }
}

export default function Products({ products }) {
  return (
    <>
      <h1>Товары</h1> 
      <ul>
        {products.map((product) => (
          <li key={product.id}>{product.name}</li>
        ))}
      </ul>
    </>
  )
}

3. Страница конкретного товара (Dynamic Routes + ISR)

Для страниц вида /products/[id] используется getStaticPaths. Это позволяет создать все страницы товаров на этапе сборки.

Проблема: Если у вас 100,000 товаров, сборка займет вечность. Решение (Incremental Static Generation): Мы можем использовать параметр fallback.

  • fallback: true: Если страницы с таким ID еще нет, Next.js покажет «заглушку» (loading), а в фоне сгенерирует страницу и сохранит её в кэш. Следующий пользователь получит её мгновенно.
  • fallback: false: Если страницы нет — будет 404 ошибка.

// pages/products/[id].js

export async function getStaticPaths() {
  const products = await getProductsFromDatabase()
  const paths = products.map((product) => ({
    params: { id: product.id }
  }))

  return { paths, fallback: true }
}

export async function getStaticProps({ params }) {
  return {
    props: {
      product: await getProductFromDatabase(params.id)
    },
    // REVALIDATE: Обновление страницы (Incremental Regeneration)
    // Каждые 60 секунд Next.js будет проверять, не изменились ли данные.
    // Если данные изменились, он пересоберет страницу в фоне.
    revalidate: 60 
  }
}

import { useRouter } from 'next/router'

export default function Product({ product }) {
  const router = useRouter()

  // Если страница еще генерируется (fallback mode)
  if (router.isFallback) {
    return <div>Загрузка...</div>
  }

  return <div>{/* Рендер товара */}</div>
}

4. Корзина (Client-side Fetching)

Страница корзины уникальна для каждого пользователя. Вместо тяжелого SSR лучше использовать статическую оболочку и загружать данные на клиенте с помощью библиотеки SWR.


import useSWR from 'swr'

function ShoppingCart() {
  // SWR берет на себя кэширование и ревалидацию
  const { data, error } = useSWR('/api/cart', fetchAPI)

  if (error) return <div>Ошибка загрузки</div>
  if (!data) return <div>Загрузка...</div>
  
  return <div>В корзине товаров: {data.products.length}</div>
}


Работа с данными (Запись данных через API Routes)

Приложению нужно не только читать, но и записывать данные (например, добавить товар в корзину). Для этого в Next.js используются API Routes. Файлы внутри pages/api/ создают полноценные серверные эндпоинты.


// pages/api/cart.js
export default async (req, res) => {
  const response = await fetch(`https://.../cart`, {
    body: JSON.stringify({
      productId: req.query.productId
    }),
    headers: {
      'Authorization': `Token ${process.env.YOUR_API_KEY}`,
      'Content-Type': 'application/json'
    },
    method: 'POST'
  })

  const { products } = await response.json()
  return res.status(200).json({ products })
};

Преимущество: API Routes работают как Serverless-функции, что позволяет безопасно использовать секретные ключи (Environment Variables), не раскрывая их клиенту.

Итоги

Использование Static Generation дает:

  1. Стабильную скорость: Страницы всегда загружаются быстро.
  2. Глобальное кэширование: HTML можно раздавать через CDN.
  3. Отказоустойчивость: Если база данных «упадет», пользователи всё равно увидят закэшированные страницы.
  4. Снижение нагрузки на бэкенд: База не перегружается запросами при каждом клике пользователя.