All Systems Operational
Powered By
profound-logo
profound-logoProfound CMS
⌘K
Admin

Статическая отрисовка с поддержкой режима редактирования

объясняет, как сайт документации обеспечивает быструю загрузку статических страниц, сохраняя функциональность режима редактирования CMS

Continue Reading
Previous‹Настройка Прокси Админ ПанелиNextСкриптинг в конструкторе шаблонов›

Гибрид

Проект РендерераПараметрическая МаршрутизацияТипы КомпонентовSseНастройка Прокси Админ ПанелиСтатическая отрисовка с поддержкой режима редактированияСкриптинг в конструкторе шаблоновCreate Profound Next

Безголовый

Быстрый стартJson И Claude КодКомпонент Zod Pull

Mcp

Mcp

Возможности CMS

Feat Docs TemplateFeat Конструктор ШаблоновFeat ПереводчикFeat Организация

Мотивация

Наш подход

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

Гибрид Против Headless

В этом руководстве объясняется, как сайт документации обеспечивает быструю загрузку статических страниц, сохраняя при этом функциональность режима редактирования CMS.

Архитектура с двумя маршрутами

Мы используем два маршрута для одного и того же контента:

app/
├── [...slug]/page.tsx          # Продакшн: force-static (быстро)
└── preview/[...slug]/page.tsx  # Режим редактирования: force-dynamic (читает searchParams)

Промежуточное ПО прокси прозрачно переписывает запросы в режиме редактирования:

Пользователь запрашивает: /en/headless/quickstart?edit_mode=true
       ↓
Прокси обнаруживает ?edit_mode=true
       ↓
Внутренний редирект на: /preview/en/headless/quickstart?edit_mode=true
       ↓
Динамический маршрут рендерится с оболочками редактирования

Пользователь никогда не увидит /preview в своём URL — это внутреннее переписывание.

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

Путь продакшна (по умолчанию)

// app/[...slug]/page.tsx
export const dynamic = 'force-static';
export const revalidate = 60;

export default async function Page({ params }) {
  // Нет searchParams — страница полностью статическая
  
  • Страницы предварительно рендерятся во время сборки
  • Мгновенно отдаются из кэша на краю CDN
  • Каждые 60 секунд выполняется повторная валидация (ISR) в качестве страховки

Путь режима редактирования

// app/preview/[...slug]/page.tsx
export const dynamic = 'force-dynamic';

export default async function PreviewPage({ params, searchParams }) {
  // searchParams доступны — можно определить edit_mode
  return <ParametricRoutePage params={params
  • Рендерится при каждом запросе
  • Может считывать ?edit_mode=true из URL
  • Рендерит с редактируемыми оболочками CMS и контурами блоков

Переписывание в прокси

// src/proxy.ts
export const proxy = async (request: NextRequest) => {
  const { pathname, searchParams } = request.nextUrl;

  const editMode = searchParams.get('edit_mode');

Сравнение производительности

СценарийВремя ответаРендеринг
Страница продакшна (из кэша)~50-100msСтатическая из CDN
Страница продакшна (устаревшая)~50-100ms + фоновое обновлениеСтатическая, затем ISR
Режим редактирования~500-1500msДинамический SSR

Понимание ISR (Incremental Static Regeneration)

Параметр revalidate = 60 включает ISR. Это НЕ означает, что страницы пересчитываются каждые 60 секунд.

ISR использует шаблон stale-while-revalidate:

Поступает запрос:
  → Кэшированная страница моложе 60 с? → Отдаём из кэша (мгновенно)
  → Кэшированная страница старше 60 с? → Отдаём устаревший кэш (мгновенно)
                                + выполняем повторную валидацию в фоне для следующего запроса

Нет запросов = Нет перерасчёта. Никогда.

Пример:

  • Страница закэширована в 10:00
  • Нет посетителей до 15:00
  • Первый посетитель в 15:00 мгновенно получает устаревший кэш
  • В фоне происходит обновление, следующий посетитель получает свежий контент

Интервал в 60 секунд — это страховочная сетка. В идеале мы бы использовали повторную валидацию по требованию через вебхуки при изменении контента в CMS.

Когда использовать каждый маршрут

Сценарий использованияURLИспользуемый маршрут
Обычный просмотр/en/headless/quickstartСтатический
Конструктор шаблонов CMS/en/headless/quickstart?edit_mode=trueПредпросмотр (через переписывание)
Предпросмотр AI-блока/en/headless/quickstart?ai_preview=1Предпросмотр (через переписывание)

Почему не использовать только динамический рендеринг?

Простой подход (без force-static, без маршрута preview) работает, но медленный:

// Простой подход — работает, но ~1-2 с на запрос
export default async function Page({ params, searchParams }) {
  return <ParametricRoutePage params={params} searchParams={searchParams} />;
}

Он рендерит динамически на каждый запрос. Для сайта документации с большим количеством страниц это означает:

  • холодный старт на каждой странице
  • отсутствие преимущества кэширования на краю CDN
  • время загрузки 1-2 секунды вместо миллисекунд

Архитектура с двумя маршрутами даёт нам лучшее из обоих миров: статическую скорость для читателей и динамическую функциональность для редакторов.

return
<
ParametricRoutePage
params
={
params
} />;
}
}
searchParams
={
searchParams
} />;
}
const
aiPreview
=
searchParams.
get
(
'ai_preview'
);
// Перенаправляем на маршрут preview, если присутствует edit_mode или ai_preview
if ((editMode === 'true' || editMode === '1' || aiPreview) && !pathname.startsWith('/preview')) {
const url = request.nextUrl.clone();
url.pathname = `/preview${pathname}`;
return NextResponse.rewrite(url);
}
return NextResponse.next();
};