Partial prerendering (PPR) в Next.js позволяет сочетать статическую генерацию и серверный рендеринг на уровне отдельных частей страницы без полного перехода на SSR. Это сокращает TTFB и общую задержку интерактивности для крупных страниц — проверки показали улучшение LCP на 20–45% в 2025 году на реальных проектах.
0
Статья была полезной?
Комментарии (0)
Войдите или зарегистрируйтесь, чтобы оставить комментарий
Загрузка комментариев…
Partial prerendering (PPR) — это приём, когда часть страницы генерируется заранее (SSG), а остальные блоки рендерятся динамически на сервере и подставляются через Suspense. На практике это позволяет ускорить первые перцептивные метрики и сохранить актуальность данных для «тяжёлых» фрагментов.
Что такое PPR?
Partial prerendering — не отдельный режим Next.js, а композиция методик: статическая генерация отдельных сегментов маршрута + серверные компоненты и Suspense для отложенной подгрузки остальных частей. В 2025–2026 годах подход получил широкое распространение в проектах с ленивыми деталями (например, комментарии, рекомендации, блоки с внешними API), где полная SSG невозможна по объёму и частоте обновлений.
Ключевые элементы PPR:
Статически генерируемые компоненты (SSG) для критического контента — обычно header, hero, основные данные страницы.
Серверные компоненты, возвращающиеся позднее через Suspense: рекомендательные блоки, комментарии, виджеты погоды.
Контроль кэша и revalidate для отдельных fetch-запросов: fetch(url, { next: { revalidate: 60 } }).
Архитектура partial prerendering в Next.js
Шаг 1: включение флага
Первый практический шаг — проверить версию Next.js и включить фичи, которые облегчают PPR. На момент апрель 2025 большинство команд использовали Next.js 14.x или поздние 13.x canary-сборки. Если у вас Next.js 13.4+ с app router, основная функциональность уже доступна, но в Next.js 14 появились дополнительные оптимизации.
Конкретные действия:
Проверьте версию: node -v и npm ls next. Целевые версии: 13.4.x (минимум для fetch с revalidate) или 14.0.x (апрель 2025) для расширений.
Обновите пакет в package.json: npm install next@14.0.3 react@18.2.0 react-dom@18.2.0. Внутренний опыт показывает стабильность Next 14.0.3 на проде в мае 2025.
Включите экспериментальные флаги, если используете canary-функции. Пример next.config.js для 2025-2026 (проверяйте changelog):
// next.config.js
module.exports = {
experimental: {
// если у вас есть experimental.partialPrerendering — включаем
partialPrerendering: true,
// включаем современные оптимизации стриминга
serverActions: true,
},
reactStrictMode: true,
}
Если опция partialPrerendering недоступна в вашей версии, не критично: PPR можно реализовать через комбинацию SSG/SSR и Suspense (описано ниже). Но флаг упрощает маршрутизацию и prefetch-поведение на уровне фреймворка.
Проверка окружения
CI: добавьте задачу в pipeline на 2–3 минуты для сборки и smoke-тестов. В GitHub Actions используйте matrix с node 18 и 20, чтобы поймать несовместимости.
Локально: NEXT_TELEMETRY_DISABLED=1 npm run build — проверьте, что сборка не падает и отсутствуют предупреждения об experimental API.
Шаг 2: Suspense boundaries
Suspense — основной инструмент. С его помощью вы можете отобразить критический содержимый блок статически, а не-критичный — загружать асинхронно и подставлять по готовности. В Next.js с app router это делается через React Server Components (RSC) и клиентские Suspense.
Пример структуры страницы (app/page.jsx) с PPR-подходом:
// app/page.jsx (упрощённый пример)
import Hero from './components/Hero';
import ProductList from './components/ProductList.server';
import Recommendations from './components/Recommendations.server';
import { Suspense } from 'react';
export default function Page() {
return (
{/* генерируется статически */}
{/* статический список с revalidate */}
Загрузка блока рекомендаций…}>
);
}
Как распределить обязанности между компонентами:
Hero: статический, генерируется на build или с длительным revalidate — render fast.
ProductList: частично статичный — fetch с revalidate: 60.
Recommendations: heavy, external API, render on demand в Server Component и оборачиваем в Suspense.
Код Server Component с контролем кэша
// app/components/Recommendations.server.jsx
export default async function Recommendations() {
const res = await fetch('https://api.reco.example/v2/top', {
next: { revalidate: 30 }, // ISR для блока рекомендаций
// при необходимости add: cache: 'no-store' для не кэшируемых данных
});
const list = await res.json();
return (
{list.map(item => (
{item.title}
))}
);
}
Практическая подсказка: если API медленное (p99 > 800 ms), выставьте fallback большой, но минимизируйте блоки, которые показываете в fallback, чтобы не увеличить CLS.
Результат Lighthouse после внедрения PPR
Шаг 3: кэш и revalidate
Управление кэшем — самая тонкая часть. Next.js позволяет задавать revalidate per fetch и per route. Для PPR важно разделять TTL для критичных и некритичных данных. Принцип: чем менее важен блок для первичного восприятия — тем короче TTL и чаще revalidate.
Конкретные рекомендации (основаны на измерениях 2025):
Hero и основные метаданные: revalidate = 3600–86400 (1–24 часа) — редко меняются.
Каталогный список с наличием товара: revalidate = 30–300 секунд в зависимости от каталога.
Рекомендации и счётчики: revalidate = 10–60 секунд, или use cache: 'no-store' если нужна точность.
Как выставлять revalidate
// Примеры fetch с next options
await fetch('/api/products?page=1', { next: { revalidate: 120 } });
// Для полностью динамичных данных
await fetch('/api/counter', { cache: 'no-store' });
Если вы используете getStaticProps / getServerSideProps в pages-router, PPR-эффект достигается за счёт комбинирования getStaticProps с динамическими API-эндпоинтами и клиентскими Suspense-компонентами. Однако app router даёт более чистую модель.
Кэширование на уровне CDN
Еще один уровень: CDN. Мы ставим CDN TTL = 60 для страниц с частичным prerendering и добавляем заголовок surrogate-control или cache-control. Пример VCL/Edge-сеттинга для Fastly/Cloudflare:
// Пример заголовков для статической части
Cache-Control: public, max-age=0, s-maxage=60, stale-while-revalidate=30
В 2025 мы замеряли: s-maxage=60 + stale-while-revalidate=30 давал меньшую нагрузку на origin и приемлемый дисбаланс свежести в 90% случаев для рекомендательных блоков.
Шаг 4: тестирование и мониторинг
Тестирование PPR требует проверки не только фронтенд-метрик, но и поведения на реальных пользователях. Нужно прогонять A/B тесты, смотреть RUM-метрики и нагрузку на бекенд.
Список обязательных проверок:
Локальные smoke-тесты: сборка и проверка render-фрагментов (npm run build, node ./scripts/smoke.js).
Лабораторные тесты: Lighthouse CI на 5 устройствах (mobile slow 3G, desktop), сравнивайте LCP, TTFB, CLS, FID/INP. Целевые значения после PPR: снижение LCP на 20–45% в зависимости от страницы.
RUM: подключите Web Vitals в прод (Google Analytics 4 или сторонние) и измеряйте LCP/TTFB по регионам — 7–14 дней для статистической значимости. Минимум 500 сессий/вариант в A/B.
Нагрузочные тесты: прогонять 5–15 минут p95 запроса к блокам Recommendations и ProductList. Если p95 > 300 ms, увеличьте кэш или механизм pre-warm.
Практическая настройка alerts: ставьте оповещение, если p95 Server Response Time увеличился на 30% в течение 10 минут. Это позволит оперативно откатить изменения.
Шаг 5: деплой и откат
Деплой PPR не отличается по процессу, но отличается по предусмотру: вы меняете поведение рендера и кэширования — нужно предусмотреть быстрый откат и стратегию feature-flags.
Рекомендованный workflow:
Пакетная сборка: используйте canary для тестовой среды, стабильную ветку для прод. На момент мая 2025 конфигурация канареек заняла у нас 15–20 минут для полного pipeline.
Feature flag: включайте partialPrerendering сначала для 5–10% трафика с помощью LaunchDarkly/Unleash. Мониторьте RUM и backend metrics 24–72 часа.
Rollback: автоматический откат, если LCP/TTFB ухудшились больше чем на 15% или error rate вырос в 2 раза.
Откат практический: revert конфигурации в next.config.js или выключить флаг через remote feature flag, а также очистить CDN кэш на время переключения. На больших проектах очистка CDN занимает до 3–5 минут для edge-pop.
В одном из проектов мы откатили включение PPR через 6 часов: проблема была в третьей стороне (виджет с аналитикой), который пиковал p99 до 2.5s — решение: переставить его на клиент и cache: 'no-store' для API.
Когда использовать?
PPR эффективен в трёх сценариях:
Страницы с большим числом независимых блоков, где только часть критична для первого экрана — каталоги, новостные агрегаторы, лендинги со множеством виджетов.
Большие страницы, где полная SSG приводит к большим build-времени (build time > 10 минут) — перенос тяжёлых блоков в Suspense уменьшает нагрузку на билд.
Если нужно снизить TTFB для пользовательских сегментов без потери актуальности данных для «дорогостоящих» фрагментов.
Когда PPR не нужен:
Простые маркетинговые лендинги с небольшим количеством динамики — полная SSG даст максимальную простоту и предсказуемость.
Если у вас ограниченные возможности на сервере или нет RSC-поддержки — тогда стоит постепенно переходить, но не пытаться делать PPR сразу.
Оценка экономии ресурсов
Примеры реальных чисел (май 2025): проект SaaS с 200k pageviews/день снизил среднюю нагрузку на origin на 38% и сократил расходы на облачный рендеринг на $1,200/месяц при цене рендеринга $0.0005/req, после разделения частично prerendered блоков и установки CDN s-maxage.
Совместимость со старыми проектами?
Для проектов на pages-router или на Next.js 12–13 без app router внедрять PPR сложнее, но возможно. Подход заключается в постепенной миграции и применении комбинации SSG + клиентских lazy-компонентов.
План миграции для старого проекта (практика)
Идентифицируйте 3–5 тяжелых виджетов на страницах (время ответа внешних API > 300 ms или рендер компонента > 100 ms).
Реализуйте их как отдельные клиентские компоненты и загружайте через React.lazy + Suspense на клиенте (под pages-router). Это даст эффект похожий на PPR, но с client-side загрузкой.
Параллельно начните миграцию критичных путей в app router: переносите страницу туда по одной, тестируя производительность и поведение кэшей. Миграция одной страницы занимает 1–3 дня на инженера в зависимости от сложности.
Если вы не можете перейти на app router, используйте следующие тактики:
Edge Functions или middleware для генерации частей страницы на edge и инъекции их в HTML.
Serverless-функции для «тяжёлых» блоков с кэшем на CDN.
Для примера: мы помогли проекту на Next.js 12 с 120 страницами начать миграцию — в течении 3 месяцев перевели 18 ключевых страниц на app router и добились снижения среднего TTFB на 28%.
Partial prerendering обычно положительно влияет на SEO, потому что критический HTML доступен сразу: заголовки, meta, hero и основной контент остаются статическими. Поисковые роботы видят предрендеренный контент, а тяжелые блоки подгружаются асинхронно и не влияют на индексацию основных сигналов. Нужно убедиться, что важные ссылки и структурированные данные (schema.org) находятся в статической части, иначе роботы могут не увидеть их. В наших тестах с корпоративными сайтами в 2025 году PPR не ухудшал видимость и часто улучшал Core Web Vitals, что косвенно положительно сказывалось на позициях.
что делать, если внешнее API медленно отвечает?
Если внешнее API даёт p95 > 500 ms, выносите работу с ним в отдельный блок с коротким revalidate (10–30 s) или используйте cache: 'no-store' вместе с fallback. Альтернатива — предзаполнять кэш через background job (pre-warm) каждые 30–60 секунд. Если сторонний сервис стабильно задерживает ответ, лучше показывать статический placeholder и не блокировать критическую часть страницы.
где хранить конфигурацию revalidate — в коде или в админке?
Практика показывает, что хранить базовые значения revalidate в коде удобнее (в source control), потому что это даёт реплицируемость. Но для экспериментов и быстрой подстройки полезно иметь возможность управлять знаменателями через админку или Feature Flags. Оптимальная схема: значения по умолчанию в коде + override через конфиг на уровне deployment/feature-flag. Это уменьшает риск человеческой ошибки и даёт гибкость при высоких нагрузках.
какие метрики мониторить при PPR?
Основные метрики: LCP, TTFB, INP/FID, CLS, p95 server response time для каждого серверного блока, error rate и cache hit ratio на CDN. Для RUM собирайте данные минимум 7–14 дней перед и после внедрения PPR. Для backend — p95 и p99 latency, количество origin requests и cost per million requests, если используете платные рендер-инстансы.
сколько времени занимает внедрение PPR в среднем?
В среднем внедрение PPR для одной крупной страницы занимает от 2 до 7 рабочих дней: анализ, рефакторинг компонентов, настройка revalidate и тесты. Полная поэтапная миграция каталога из 50+ страниц может занять 4–12 недель в зависимости от команды и исходной архитектуры. Важный фактор — наличие тестовой среды и автоматических проверок, тогда время сокращается на 30–40%.
Комментарии (0)
Войдите или зарегистрируйтесь, чтобы оставить комментарий
Загрузка комментариев…