Nextjs SSR vs SSG. Приготовить заранее или испечь по заказу? Гайд по рендерингу для пиццерий и разр…
- суббота, 13 сентября 2025 г. в 00:00:08
Cowabunga, друзья!
На связи Игорь, frontend-разработчик компании fuse8. Даже если вы не застали легендарный мультсериал 90-х, вы наверняка слышали о четырех черепашках-мутантах в цветных банданах, которые мастерски владеют мечом, нунчаками, парой сай и бо… и которые без ума от пиццы. Но проходят дни, пролетают года и вот уже Шреддер повержен, Крэнг изгнан, и что остается делать нашим уже далеко не подросткам черепашкам-ниндзя. Правильно! Открывать свою пиццерию.
Казалось бы — идеальный план, однако управление пиццерией — это не менее сложное задача, чем битва с силами зла.
Так их одержимость пиццей превратилась в идеальную метафору для главного вызова в современной веб-разработке: как мгновенно накормить «голодных» пользователей свежим контентом, не заставляя их томиться в ожидании у экрана? И здесь наша задача, как «разработчиков-поваров» — выбрать правильную стратегию готовки, чтобы не подвести своих клиентов в решающий момент.
В контексте Next.js с использованием App Router эти стратегии носят названия SSG (Static Site Generation) и SSR (Server-Side Rendering). И от выбора между ними напрямую зависят ключевые метрики вашего сайта: скорость загрузки, отклик, SEO и, в конечном счете, удовлетворенность пользователя. В этой статье мы поможем нашим героям-ниндзя разобраться в тонкостях пиццерийного бизнеса и параллельно разберемся, какая стратегия рендеринга подойдет для вашего проекта.
Погнали!
Леонардо как истинный лидер и стратег предложил подход, где каждый клиент сам «собирает» свою пиццу прямо на месте: выбирает начинку, тесто и добавляет специи по своему вкусу.
Client-Side Rendering (CSR) — это классический паттерн, который долгое время был основным для одностраничных приложений (SPA), построенных на фреймворках вроде React. В рамках Next.js мы можем использовать такой подход при разработке отдельных частей приложения, где это уместно, например, админ панели.
Как это работает:
Сервер отдает браузеру практически пустой HTML-файл с базовой разметкой и JS файлом.
Браузер загружает и выполняет JS.
JS в браузере делает запросы к API.
Получаем данные, JS динамически строит весь интерфейс прямо в браузере,
Проблемы подхода:
Медленная первоначальная загрузка — пока не выполнится весь JS, пользователь видит пустую страницу.
Плохая SEO-оптимизация — поисковые боты плохо индексируют, т.к. по факту к нам приходит пустой HTML.
Проблемы с медленными устройствами — на слабых телефонах рендеринг мог занимать продолжительное время.
Но есть и преимущества:
Сниженная нагрузка на сервер — весь рендер происходит на стороне устройства клиента
Глобально CSR подход отлично подходит для сайтов с высокой интерактивностью при отсутствии потребности в SEO-оптимизации. Например, веб-приложения по типу Figma.
Донателло гениален и все продумывает заранее. Он предложил наготовить горы пиццы самых ходовых вкусов. Вся пицца аккуратно разложена по коробкам, подписана и ждет своего часа в холодильнике. Как только клиент говорит «Хочу пиццу!», ему тут же достают ее из холодильника, разогревают и протягивают горячую коробку. Все предсказуемо и быстро.
Концепция SSG подхода – это отрисовывать страницы во время сборки, а именно – в момент запуска next build. Это означает, что процесс создания веб-страниц происходит до запуска сайта и получения пользователями доступа к нему, т.е. HTML и контент для каждой страницы предварительно отрендерены или сгенерированы. Когда пользователь заходит на страницу вашего сайта, предварительно отрендеренный статический HTML мгновенно доставляется в его браузер без какого-либо дополнительного выполнения кода или обработки данных в момент его запроса.
По умолчанию Next.js старается генерировать страницы статически. Давайте посмотрим, как это будет выглядеть в коде. Для примера создадим простую главную страницу:
import Link from "next/link";
export default async function MainPage() {
return (
<div>
<Link href="/turtles">All turtles</Link>
</div>
);
}
Запустив в дальнейшем команду next build
, мы убедимся, что главная страница и страница «not-found» сгенерированы как «чистая» статика. Такое поведение типично, когда страница не зависит от каких-либо данных или планирует получать их на клиенте.
Я осознаю всю ответственность – разговоры про пиццу уж очень сильно разогревают аппетит, и повышается риск того, что вместо поглощения знаний вы сорветесь в ближайшую пиццерию, где захотите поглотить сытнейшую 4 сыра «Мазеррати». Поэтому давайте немного отойдем от пицц и «заготовим» страницы для каждой черепашки, где будем выводить их ключевые качества и оружие.
Для этого создадим следующий динамический роут:
В рамках SSG подхода мы используем специальную функцию generateStaticParams (можно сказать, что это инструкция, которая говорит NEXT.js, какое количество и какие страницы «заготовить» заранее) и сделаем запрос к API для получения данных:
export async function generateStaticParams() {
const response = await fetch("https://www.turtletime.dev/api/v1/turtles");
const turtles = await response.json();
return turtles.map((turtle) => ({
name: turtle.name,
}));
}
export default async function TurtlePage({ params }) {
const { name } = params;
const response = await fetch(
https://www.turtletime.dev/api/v1/turtles/${name}
);
const turtle = await response.json();
return (
<div>
<h1>{turtle.name}</h1>
<p>Personality: {turtle.personality}</p>
<p>Weapon: {turtle.weapon}</p>
</div>
);
}
Запустив команду next-build
, получим следующую картину:
Здесь мы видим, что во время сборки генерируются все известные страницы для черепашек. Все запросы к этим страницам (например, /turtles/donatello
) кэшируются, чтобы в дальнейшем обрабатываться мгновенно.
Плюсы SSG подхода:
Производительность. Мгновенная скорость загрузки. Пользователь получает готовую страницу сразу. Time to First Byte (TTFB) — минимальный.
Небольшая нагрузка на сервер, так как он просто отдает уже готовый файл.
SEO дружелюбность. С сервера к нам приходит готовая HTML страница, которая хорошо индексируется поисковыми роботами.
Минусы подхода:
Риск устаревания данных. Данные актуальны только на момент сборки.
Ограниченное обновление в режиме реального времени. При использовании SSG вы не сможете эффективно изменять или вносить обновления в режиме реального времени на свой сайт.
Плохая масштабируемость. Не очень хорошо работает, если нужно заранее «заготавливать» миллионы страниц.
SSG стратегия идеально подходит для страниц «О нас», контактов, блогов, документации — всего контента, который не меняется слишком часто.
Возвращаемся к пиццам. Пока Донателло возился со своими заготовками и холодильниками, Рафаэль лишь хрипло усмехнулся. Он убежден, что секрет коммерческого успеха пиццерии в индивидуальном подходе, и что настоящая пицца должна быть с пылу, с жару. «Мы будем делать всё сами! От теста для пиццы, до выращивания сочных и спелых томатов в каморке учителя Сплинтера. Никаких заготовок, каждый заказ — это новая битва, где каждый клиент заслуживает пиццу, приготовленную специально для него,» – сказал Рафаэль и отправился осуществлять задуманное.
Вот что происходит на нашей Next.js «кухне», когда мы выбираем стратегию SSR. В отличие от SSG, серверный рендеринг фокусируется на странице и создаёт её в режиме реального времени. В этом случае, когда вы посещаете веб-сайт, сервер, на котором он размещен, создаёт страницу для вас, подобно тому, как Рафаэль готовит вкуснейшую пепперони по вашему индивидуальному заказу.
Как это работает:
Браузер отправляет запрос на сервер для получения HTML-страницы
Сервер, получив запрос, немедленно приступает к рендерингу, объединяет все части (данные и HTML-разметку) в один полностью готовый HTML-документ
Сервер отправляет готовый HTML-документ обратно в браузер пользователя
Браузер получает страницу в ее окончательном виде и немедленно отображает её пользователю. Это обеспечивает быструю первоначальную загрузку.
После отображения HTML страница обычно "оживает": загружаются и выполняются JavaScript-файлы, чтобы сделать страницу интерактивной. Этот процесс называется гидратацией.
Самый простой способ активации SSR — это отказаться от использования generateStaticParams
в прошлом примере, и тогда страница автоматически будет генерироваться как динамическая (server-rendered on demand
).
Помимо этого мы можем добиться такого поведения передав в fetch дополнительные опции, а именно cache: “no-store”.
Давайте создадим страницу, на которой будем отображать список всех черепашек и их любимых пицц:
import Link from "next/link";
export default async function TurtlesPage() {
const res = await fetch("https://www.turtletime.dev/api/v1//turtles",
{
cache: "no-store",
});
const turtles = await res.json();
return (
<div>
<h1>Turtles</h1>
<ul>
{turtles.map((turtle, index) => (
<li key={index}>
<Link href={/turtles/${turtle.name}}>
{turtle.name} - {turtle.favorite_pizza}
</Link>
</li>
))}
</ul>
</div>
);
}
Стоит также отметить, что добиться такого состояния можно и через использование в компоненте export const dynamic = 'force-dynamic'
(это эквивалентно установке для каждого запроса на странице параметра {cache: 'no-store', next: { revalidate: 0 } }
) или следующих API:
cookies;
headers;
connection;
draftMode;
searchParams prop;
unstable_noStore.
Запустив next build
, мы видим, что страница стала динамической. Об этом сигнализирует символ ƒ — (Dynamic) server-rendered on demand
.
Плюсы SSR подхода:
SEO-дружелюбность. Также как и при SSG мы получаем готовый HTML, который индексируется поисковиками
Динамический контент. SSR позволяет генерировать контент в режиме реального времени, обеспечивая мгновенные обновления и персонализированный опыт.
Не требователен к устройству клиента. Весь процесс преобразования кода и данных в HTML (рендеринг) происходит на сервере в момент запроса.
Минусы подхода:
Увеличенная нагрузка на сервер. SSR может создавать дополнительную нагрузку на серверные ресурсы, особенно при большом количестве пользовательских запросов, что может повлиять на производительность.
Клиент ждет, пока страницу «приготовят». Даже если это произойдет очень быстро, клиенту придется провести какое-то время в ожидании (более высокий Time to First Byte по сравнению с SSG)
SSR очень хорошо подходит для товаров интернет-магазина. Так как для товаров важно SEO, а самих товаров может быть очень много, генерировать их все заранее будет ресурсозатратно.
Говоря про основные стратегии рендеринга сайта стоит также упомянуть про ISR подход. И пока Донателло и Рафаэль спорили, чья стратегия лучше, Микеланджело, как большой любитель поесть и придумать что-то крутое, нашел гениальное решение: «Чуваки! А что, если мы будем хранить готовую пиццу на специальной подложке, которая по необходимости будет незаметно для клиента подогревать пиццу, как только она начнет остывать?»
Также как и в SSG мы заранее «заготавливаем» наши страницы в рамках ISR подхода. И тут может возникнуть вопрос, а как долго мы можем хранить нашу заготовленную пиццу?
Revalidate — директива, которую вы задаете в коде (например, revalidate: 3600). Это указание для вашего сервера Next.js, как часто нужно проверять актуальность данных и пересобирать эту конкретную страницу (подогревать пиццу). Давайте добавим эту строчку кода в наш пример с SSG.
// Next.js будет аннулировать кэш при поступлении
// запроса, не чаще одного раза в час.
export const revalidate = 3600;
Как это работает:
когда пользователь запрашивает страницу, Next.js смотрит на эту директиву.
если с момента последней “выпечки” прошло меньше секунд чем указано в revalidate — пользователь мгновенно получает страницу из кеша.
если срок истек — Next.js точно так же мгновенно отдает пользователю последнюю «испеченную» версию (пусть и условно “просроченную”), но параллельно запускает фоновый процесс, чтобы “подогреть” новую, свежую страницу с актуальными данными. Следующий пользователь получит уже её.
Добавляя revalidate мы можем автоматически обновлять страницы по таймеру — Time-based revalidation
. Помимо этого мы также можем обновлять кэш по запросу — On-Demand Revalidation
.
On-Demand Revalidation
— это наша секретная кнопка «Подогрев пиццы». Например, вы через админку добавляете новый пост в блог. Админка тут же «нажимает» на эту кнопку, отправляя специальный сигнал на наш сервер. Сигнал мгновенно помечает конкретную страницу как «устаревшую». Теперь её «пересоберут» при самом первом следующем запросе, даже если по revalidate таймеру ее »срок годности» еще не вышел.
Есть два способа сбросить кэш: по пути страницы и по тэгу (revalidatePath
и revalidateTag
).
Для этого создадим две папки с роутами:
В папке revalidation-on-demand
создадим страницу в которой будем отображать время «приготовления» нашей страницы:
export default async function RevalidationOnDemand() {
return (
<>
<p> Эта страница сгенерирована в:
{new Date().toLocaleTimeString()}
</p>
</>
);
}
Также добавим API роут который будет ревалидировать(сбрасывать) кэш при запросе:
import { revalidatePath } from "next/cache";
import { NextResponse } from "next/server";
export async function GET() {
revalidatePath(`/revalidation-on-demand`);
return NextResponse.json({
revalidated: true,
now: Date.now()
});
}
Запускаем next build → next start → открываем роут /revalidation-on-demand
для загрузки даты первый раз (если вы попробуете обновить страницу, то время останется таким же) → чтобы обновить кэш по пути (revalidatePath) переходим на роут /api/revalidate (это кнопка «Подогрев пиццы») → после этого следующее обновление страницы /revalidation-on-demand
покажет свежую дату.
Преимущественно revalidatePath
подходит, когда вам нужно обновить одну конкретную страницу, а revalidateTag
используем, когда одни и те же данные используются на нескольких страницах, или вы не знаете все возможные пути, которые зависят от измененных данных
Ещё один наглядный пример того, как работает On-Demand Revalidation
можно посмотреть здесь (просто создайте новое issue или комментарий в репозитории и перезагрузите страницу).
Такой подход идеально подходит для страниц, которым важно и SEO, и предварительная генерация, и частое обновление контента. Например, это могут быть контентные-сайты (блоги, новостные порталы), где мы можем заранее сгенерировать все статьи (SSG), а при публикации новой (например, через админку) или правке существующей статьи — мгновенно инвалидировать кеш именно для этой страницы, не пересобирая весь сайт.
Спор между Донателло (SSG) и Рафаэлем (SSR) кажется вечным. Но как мудрый учитель, мастер Сплинтер знает: не существует плохой или хорошей стратегии — есть уместная. Правильный выбор зависит от конкретных требований вашего проекта.
Чтобы принять взвешенное решение, задайте себе следующие вопросы:
1. Как часто обновляется контент?
Выбирайте SSG с ревалидацией (ISR), если контент на сайте обновляется относительно редко (например, раз в день, неделю или реже). Идеально для блогов, маркетинговых сайтов и каталогов продуктов, где информация статична после публикации.
Выбирайте SSR для страниц, которые невозможно подготовить заранее из-за их количества или персонализации.
2. Требуются ли персонализированные данные для каждого пользователя?
Выбирайте SSG, если содержание страницы одинаково для всех посетителей (например, статья, страница «О нас», контакты).
Выбирайте SSR, если вам необходимо показывать уникальный контент в зависимости от пользователя (его геолокации, данных в cookie, статуса авторизации).
3. Откуда и какие данные вам нужны?
Выбирайте SSG, если данные для страницы можно получить во время сборки проекта из CMS, файловой системы или стороннего API.
Выбирайте SSR, если для рендеринга страницы необходимы данные из самого запроса (например, cookies, заголовки, параметры URL), которые невозможно получить на этапе сборки.
Итак, наша пиццерия Next.js готова к любым вызовам, а команда шеф-поваров прошла полный курс подготовки. Мы усвоили главные уроки на примере наших героев-ниндзя:
Стратегия Донателло (SSG) — используем её для всего, что можно «заготовить» заранее и получаем склад пицц (страниц) с быстрой доставкой до конечного клиента.
Тактика Рафаэля (SSR) — мы используем этот подход, когда каждый заказ должен быть уникальным и собран из самых свежих, только что полученных ингредиентов (данных), никаких полуфабрикатов. Страница будет «приготовлена» специально для вас в реальном времени, как только вы сделаете запрос.
Изобретение Микеланджело (ISR) — это своего рода золотая середина. Этот подход позволяет сочетать скорость статики (SSG) и актуальность динамического SSR рендеринга.
А как же Леонардо с его легендарным CSR подходом, спросите вы? Next.js предлагает использовать его избирательно, комбинируя с остальными подходами для улучшения производительности, SEO и пользовательского опыта.
Как итог, не существует единственно правильного выбора. Есть тот инструмент, который идеально подходит для конкретной задачи на вашем сайте. Ведь как говаривал учитель Сплинтер: «Начинай с SSG. Действуй от простого к сложному. И подключай SSR только тогда, когда без персонального подхода действительно не обойтись».
Пусть ваш сайт будет быстрым, как удар Рафаэля и надежным, как план Донателло! Готовьте свои проекты со вкусом, и пусть ваши пользователи всегда остаются сытыми и довольными.
Cowabunga! 🍕
P.S. Если понравился такой формат, то оставляйте комментарии и вскоре разберём новую экспериментальную фичу NEXT.js — Partial Prerendering.