Режим предпросмотра

Примечание: Эта функция заменена Режимом черновика (Draft Mode).

Примеры

В документации по страницам (Pages) и документации по получению данных (Data Fetching) мы обсуждали, как предварительно отрендерить страницу во время сборки (Статическая генерация (Static Generation)) с использованием getStaticProps и getStaticPaths.

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

Next.js предоставляет функцию Режим предпросмотра (Preview Mode), которая решает эту проблему. Ниже приведены инструкции по её использованию.

Шаг 1: Создание и доступ к API-маршруту предпросмотра

Ознакомьтесь с документацией по API-маршрутам, если вы не знакомы с API-маршрутами Next.js.

Сначала создайте API-маршрут предпросмотра. Он может иметь любое имя, например pages/api/preview.js (или .ts, если используется TypeScript).

В этом API-маршруте вам нужно вызвать setPreviewData для объекта ответа. Аргумент для setPreviewData должен быть объектом, и он может использоваться в getStaticProps (подробнее об этом позже). Пока мы будем использовать {}.

export default function handler(req, res) {
  // ...
  res.setPreviewData({})
  // ...
}

res.setPreviewData устанавливает некоторые куки в браузере, которые включают режим предпросмотра. Любые запросы к Next.js, содержащие эти куки, будут рассматриваться как режим предпросмотра, и поведение для статически сгенерированных страниц изменится (подробнее об этом позже).

Вы можете протестировать это вручную, создав API-маршрут, как показано ниже, и обратившись к нему вручную из браузера:

pages/api/preview.js
// Простой пример для ручного тестирования из браузера.
export default function handler(req, res) {
  res.setPreviewData({})
  res.end('Режим предпросмотра включен')
}

Если вы откроете инструменты разработчика в браузере и перейдёте по адресу /api/preview, вы заметите, что куки __prerender_bypass и __next_preview_data будут установлены для этого запроса.

Безопасный доступ из вашей Headless CMS

На практике вам нужно вызывать этот API-маршрут безопасно из вашей headless CMS. Конкретные шаги будут зависеть от используемой headless CMS, но вот некоторые общие действия, которые вы можете предпринять.

Эти шаги предполагают, что ваша headless CMS поддерживает настройку пользовательских URL-адресов предпросмотра. Если нет, вы всё равно можете использовать этот метод для защиты URL-адресов предпросмотра, но вам нужно будет вручную создавать и обращаться к URL-адресу предпросмотра.

Во-первых, создайте секретный токен с помощью генератора токенов. Этот секрет будет известен только вашему приложению Next.js и вашей headless CMS. Это предотвратит доступ к URL-адресам предпросмотра для тех, у кого нет доступа к CMS.

Во-вторых, если ваша headless CMS поддерживает настройку пользовательских URL-адресов предпросмотра, укажите следующий URL-адрес в качестве адреса предпросмотра. Предполагается, что ваш API-маршрут предпросмотра находится по адресу pages/api/preview.js.

Терминал
https://<ваш-сайт>/api/preview?secret=<токен>&slug=<путь>
  • <ваш-сайт> должен быть вашим доменом развёртывания.
  • <токен> должен быть заменён на сгенерированный секретный токен.
  • <путь> должен быть путём к странице, которую вы хотите предпросмотреть. Если вы хотите предпросмотреть /posts/foo, используйте &slug=/posts/foo.

Ваша headless CMS может позволить вам включить переменную в URL-адрес предпросмотра, чтобы <путь> мог быть установлен динамически на основе данных CMS, например: &slug=/posts/{entry.fields.slug}

Наконец, в API-маршруте предпросмотра:

  • Проверьте, что секрет совпадает и что параметр slug существует (если нет, запрос должен завершиться ошибкой).
  • Вызовите res.setPreviewData.
  • Затем перенаправьте браузер на путь, указанный в slug. (В следующем примере используется 307 редирект).
export default async (req, res) => {
  // Проверка секрета и параметров
  // Этот секрет должен быть известен только этому API-маршруту и CMS
  if (req.query.secret !== 'MY_SECRET_TOKEN' || !req.query.slug) {
    return res.status(401).json({ message: 'Неверный токен' })
  }

  // Запрос к headless CMS для проверки существования указанного `slug`
  // getPostBySlug реализует необходимую логику запроса к headless CMS
  const post = await getPostBySlug(req.query.slug)

  // Если slug не существует, предотвратите включение режима предпросмотра
  if (!post) {
    return res.status(401).json({ message: 'Неверный slug' })
  }

  // Включение режима предпросмотра путём установки кук
  res.setPreviewData({})

  // Перенаправление на путь из полученного поста
  // Мы не перенаправляем на req.query.slug, чтобы избежать уязвимостей открытого перенаправления
  res.redirect(post.slug)
}

Если всё прошло успешно, браузер будет перенаправлен на путь, который вы хотите предпросмотреть, с установленными куками режима предпросмотра.

Шаг 2: Обновление getStaticProps

Следующий шаг — обновить getStaticProps для поддержки режима предпросмотра.

Если вы запрашиваете страницу, которая имеет getStaticProps, с установленными куками режима предпросмотра (через res.setPreviewData), то getStaticProps будет вызван во время запроса (а не во время сборки).

Кроме того, он будет вызван с объектом context, где:

  • context.preview будет true.
  • context.previewData будет таким же, как аргумент, переданный в setPreviewData.
export async function getStaticProps(context) {
  // Если вы запрашиваете эту страницу с установленными куками режима предпросмотра:
  //
  // - context.preview будет true
  // - context.previewData будет таким же,
  //   как аргумент, переданный в `setPreviewData`.
}

Мы использовали res.setPreviewData({}) в API-маршруте предпросмотра, поэтому context.previewData будет {}. Вы можете использовать это для передачи информации сеанса из API-маршрута предпросмотра в getStaticProps, если это необходимо.

Если вы также используете getStaticPaths, то context.params также будет доступен.

Получение данных предпросмотра

Вы можете обновить getStaticProps для получения разных данных в зависимости от context.preview и/или context.previewData.

Например, ваша headless CMS может иметь другой API-эндпоинт для черновиков постов. В этом случае вы можете использовать context.preview для изменения URL-адреса API-эндпоинта, как показано ниже:

export async function getStaticProps(context) {
  // Если context.preview равен true, добавьте "/preview" к API-эндпоинту
  // для запроса данных черновика вместо опубликованных данных. Это будет зависеть
  // от используемой headless CMS.
  const res = await fetch(`https://.../${context.preview ? 'preview' : ''}`)
  // ...
}

Вот и всё! Если вы обращаетесь к API-маршруту предпросмотра (с secret и slug) из вашей headless CMS или вручную, вы теперь должны видеть контент предпросмотра. И если вы обновите черновик без публикации, вы сможете предпросмотреть его.

Установите это как URL-адрес предпросмотра в вашей headless CMS или обращайтесь вручную, и вы сможете видеть предпросмотр.

Терминал
https://<ваш-сайт>/api/preview?secret=<токен>&slug=<путь>

Дополнительные детали

Полезно знать: во время рендеринга next/router предоставляет флаг isPreview, см. документацию по объекту router для получения дополнительной информации.

Указание длительности режима предпросмотра

setPreviewData принимает необязательный второй параметр, который должен быть объектом настроек. Он принимает следующие ключи:

  • maxAge: Указывает число (в секундах) для длительности сеанса предпросмотра.
  • path: Указывает путь, к которому применяется кука. По умолчанию /, что включает режим предпросмотра для всех путей.
setPreviewData(data, {
  maxAge: 60 * 60, // Куки режима предпросмотра истекают через 1 час
  path: '/about', // Куки режима предпросмотра применяются к путям с /about
})

Очистка кук режима предпросмотра

По умолчанию для кук режима предпросмотра не устанавливается дата истечения срока действия, поэтому сеанс предпросмотра завершается при закрытии браузера.

Чтобы очистить куки режима предпросмотра вручную, создайте API-маршрут, который вызывает clearPreviewData():

pages/api/clear-preview-mode-cookies.js
export default function handler(req, res) {
  res.clearPreviewData({})
}

Затем отправьте запрос к /api/clear-preview-mode-cookies для вызова API-маршрута. Если вы вызываете этот маршрут с использованием next/link, вы должны передать prefetch={false}, чтобы предотвратить вызов clearPreviewData во время предварительной загрузки ссылки.

Если в вызове setPreviewData был указан путь, вы должны передать тот же путь в clearPreviewData:

pages/api/clear-preview-mode-cookies.js
export default function handler(req, res) {
  const { path } = req.query

  res.clearPreviewData({ path })
}

Ограничения размера previewData

Вы можете передать объект в setPreviewData, и он будет доступен в getStaticProps. Однако, поскольку данные будут храниться в куки, есть ограничение на размер. В настоящее время данные предпросмотра ограничены 2 КБ.

Совместимость с getServerSideProps

Режим предпросмотра также работает с getServerSideProps. Он также будет доступен в объекте context, содержащем preview и previewData.

Полезно знать: Вы не должны устанавливать заголовок Cache-Control при использовании режима предпросмотра, потому что его нельзя обойти. Вместо этого мы рекомендуем использовать ISR.

Совместимость с API-маршрутами

API-маршруты будут иметь доступ к preview и previewData в объекте запроса. Например:

export default function myApiRoute(req, res) {
  const isPreview = req.preview
  const previewData = req.previewData
  // ...
}

Уникальность для каждого next build

И значение куки обхода, и приватный ключ для шифрования previewData изменяются при завершении next build. Это гарантирует, что куки обхода не могут быть угаданы.

Полезно знать: Для тестирования режима предпросмотра локально через HTTP ваш браузер должен разрешать сторонние куки и доступ к локальному хранилищу.