Режим черновика (Draft Mode)
В документации по страницам (Pages) и получению данных (Data Fetching) мы обсуждали, как предварительно отрендерить страницу во время сборки (Статическая генерация (Static Generation)) с помощью getStaticProps
и getStaticPaths
.
Статическая генерация полезна, когда ваши страницы получают данные из headless CMS. Однако она не идеальна, когда вы пишете черновик в headless CMS и хотите сразу просмотреть его на странице. В этом случае вам нужно, чтобы Next.js рендерил эти страницы во время запроса (request time), а не во время сборки, и получал черновой контент вместо опубликованного. Вам нужно, чтобы Next.js обходил статическую генерацию только для этого конкретного случая.
Next.js предоставляет функцию Режим черновика (Draft Mode), которая решает эту проблему. Вот инструкции по её использованию.
Шаг 1: Создание и доступ к API-маршруту
Сначала ознакомьтесь с документацией по API-маршрутам, если вы не знакомы с API-маршрутами Next.js.
Сначала создайте API-маршрут. Он может иметь любое имя, например pages/api/draft.ts
.
В этом API-маршруте вам нужно вызвать setDraftMode
для объекта ответа.
export default function handler(req, res) {
// ...
res.setDraftMode({ enable: true })
// ...
}
Это установит куки (cookie) для включения режима черновика. Последующие запросы, содержащие этот куки, будут активировать Режим черновика (Draft Mode), изменяя поведение для статически сгенерированных страниц (подробнее об этом позже).
Вы можете протестировать это вручную, создав API-маршрут, как показано ниже, и обратившись к нему вручную из браузера:
// Простой пример для ручного тестирования из браузера.
export default function handler(req, res) {
res.setDraftMode({ enable: true })
res.end('Режим черновика включен')
}
Если вы откроете инструменты разработчика в браузере и перейдёте по адресу /api/draft
, вы заметите заголовок ответа Set-Cookie
с куки под названием __prerender_bypass
.
Безопасный доступ из Headless CMS
На практике вам нужно вызывать этот API-маршрут безопасно из вашей headless CMS. Конкретные шаги будут зависеть от используемой headless CMS, но вот некоторые общие действия.
Эти шаги предполагают, что ваша headless CMS поддерживает установку пользовательских URL для черновиков. Если нет, вы всё равно можете использовать этот метод для защиты URL черновиков, но вам нужно будет создавать и получать доступ к URL черновика вручную.
Во-первых, создайте секретный токен с помощью генератора токенов. Этот секрет будет известен только вашему приложению Next.js и headless CMS. Это предотвратит доступ к черновикам для тех, у кого нет доступа к CMS.
Во-вторых, если ваша headless CMS поддерживает установку пользовательских URL для черновиков, укажите следующий URL в качестве URL черновика. Предполагается, что ваш API-маршрут для черновиков находится по адресу pages/api/draft.ts
.
https://<your-site>/api/draft?secret=<token>&slug=<path>
<your-site>
— это домен вашего развёртывания.<token>
— секретный токен, который вы сгенерировали.<path>
— путь к странице, которую вы хотите просмотреть. Если вы хотите просмотреть/posts/foo
, используйте&slug=/posts/foo
.
Ваша headless CMS может позволить вам включить переменную в URL черновика, чтобы <path>
мог динамически устанавливаться на основе данных CMS, например: &slug=/posts/{entry.fields.slug}
Наконец, в API-маршруте для черновиков:
- Проверьте, совпадает ли секрет и существует ли параметр
slug
(если нет, запрос должен завершиться ошибкой). - Вызовите
res.setDraftMode
. - Затем перенаправьте браузер на путь, указанный в
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.setDraftMode({ enable: true })
// Перенаправляем на путь из полученного поста
// Мы не перенаправляем на req.query.slug, чтобы избежать уязвимостей открытого перенаправления
res.redirect(post.slug)
}
Если всё успешно, браузер будет перенаправлен на путь, который вы хотите просмотреть, с куки режима черновика.
Шаг 2: Обновление getStaticProps
Следующий шаг — обновить getStaticProps
для поддержки режима черновика.
Если вы запрашиваете страницу, которая использует getStaticProps
, с установленным куки (через res.setDraftMode
), то getStaticProps
будет вызван во время запроса (request time) (вместо времени сборки).
Кроме того, он будет вызван с объектом context
, где context.draftMode
будет true
.
export async function getStaticProps(context) {
if (context.draftMode) {
// динамические данные
}
}
Мы использовали res.setDraftMode
в API-маршруте для черновиков, поэтому context.draftMode
будет true
.
Если вы также используете getStaticPaths
, то context.params
также будет доступен.
Получение черновых данных
Вы можете обновить getStaticProps
для получения разных данных в зависимости от context.draftMode
.
Например, ваша headless CMS может иметь другой API-эндпоинт для черновых постов. В этом случае вы можете изменить URL API-эндпоинта, как показано ниже:
export async function getStaticProps(context) {
const url = context.draftMode
? 'https://draft.example.com'
: 'https://production.example.com'
const res = await fetch(url)
// ...
}
Всё готово! Если вы обращаетесь к API-маршруту для черновиков (с secret
и slug
) из вашей headless CMS или вручную, вы теперь сможете увидеть черновой контент. И если вы обновите черновик без публикации, вы сможете просмотреть изменения.
Установите этот URL в качестве URL черновика в вашей headless CMS или обращайтесь к нему вручную, и вы сможете увидеть черновик.
https://<your-site>/api/draft?secret=<token>&slug=<path>
Дополнительные детали
Очистка куки режима черновика
По умолчанию сессия режима черновика завершается при закрытии браузера.
Чтобы очистить куки режима черновика вручную, создайте API-маршрут, который вызывает setDraftMode({ enable: false })
:
export default function handler(req, res) {
res.setDraftMode({ enable: false })
}
Затем отправьте запрос на /api/disable-draft
для вызова API-маршрута. Если вы вызываете этот маршрут с помощью next/link
, вы должны передать prefetch={false}
, чтобы случайно не удалить куки при предварительной загрузке.
Совместимость с getServerSideProps
Режим черновика работает с getServerSideProps
и доступен как ключ draftMode
в объекте context
.
Полезно знать: Не следует устанавливать заголовок
Cache-Control
при использовании режима черновика, так как его нельзя обойти. Вместо этого мы рекомендуем использовать ISR.
Совместимость с API-маршрутами
API-маршруты будут иметь доступ к draftMode
в объекте запроса. Например:
export default function myApiRoute(req, res) {
if (req.draftMode) {
// получение черновых данных
}
}
Уникальность для каждого next build
Новое значение куки для обхода будет генерироваться каждый раз при запуске next build
.
Это гарантирует, что куки для обхода нельзя угадать.
Полезно знать: Для тестирования режима черновика локально через HTTP ваш браузер должен разрешать сторонние куки и доступ к локальному хранилищу.