
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 есть два мощных инструмента:
- Incremental Static Generation (ISR): позволяет добавлять и обновлять страницы постепенно уже после этапа сборки.
- 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 дает:
- Стабильную скорость: Страницы всегда загружаются быстро.
- Глобальное кэширование: HTML можно раздавать через CDN.
- Отказоустойчивость: Если база данных «упадет», пользователи всё равно увидят закэшированные страницы.
- Снижение нагрузки на бэкенд: База не перегружается запросами при каждом клике пользователя.