跳到内容
页面路由指南预览模式

如何在 Next.js 中使用预览模式预览内容

这是一个旧版 API,不再推荐使用。为了向后兼容性,它仍然受支持。

注意:此功能已被 草稿模式 取代。

示例

页面文档数据获取文档 中,我们讨论了如何使用 getStaticPropsgetStaticPaths 在构建时预渲染页面(静态生成)。

当您的页面从无头 CMS 获取数据时,静态生成非常有用。但是,当您在无头 CMS 上编写草稿并希望在页面上立即预览草稿时,它并不理想。您希望 Next.js 在请求时而不是构建时渲染这些页面,并获取草稿内容而不是已发布内容。您希望 Next.js 仅在这种特定情况下绕过静态生成。

Next.js 有一个名为预览模式的功能,可以解决此问题。以下是使用说明。

步骤 1:创建并访问预览 API 路由

如果您不熟悉 Next.js API 路由,请首先查看 API 路由文档

首先,创建一个预览 API 路由。它可以有任何名称,例如 pages/api/preview.js(如果使用 TypeScript,则为 .ts)。

在此 API 路由中,您需要在响应对象上调用 setPreviewDatasetPreviewData 的参数应该是一个对象,getStaticProps 可以使用它(稍后会详细介绍)。现在,我们使用 {}

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

res.setPreviewData 在浏览器上设置了一些 cookie,从而开启了预览模式。任何包含这些 cookie 的 Next.js 请求都将被视为预览模式,静态生成页面的行为将发生变化(稍后会详细介绍)。

您可以通过创建如下所示的 API 路由并手动从浏览器访问它来手动测试

pages/api/preview.js
// simple example for testing it manually from your browser.
export default function handler(req, res) {
  res.setPreviewData({})
  res.end('Preview mode enabled')
}

如果您打开浏览器的开发人员工具并访问 /api/preview,您会注意到此请求将设置 __prerender_bypass__next_preview_data cookie。

从无头 CMS 安全访问

实际上,您希望从无头 CMS 安全地调用此 API 路由。具体步骤将因您使用的无头 CMS 而异,但以下是一些您可以采取的常见步骤。

这些步骤假设您使用的无头 CMS 支持设置自定义预览 URL。如果不支持,您仍然可以使用此方法保护您的预览 URL,但您需要手动构建和访问预览 URL。

首先,您应该使用您选择的令牌生成器创建一个秘密令牌字符串。此秘密将仅由您的 Next.js 应用程序和您的无头 CMS 知道。此秘密可防止无权访问您的 CMS 的人员访问预览 URL。

其次,如果您的无头 CMS 支持设置自定义预览 URL,请将以下内容指定为预览 URL。这假设您的预览 API 路由位于 pages/api/preview.js

终端
https://<your-site>/api/preview?secret=<token>&slug=<path>
  • <your-site> 应该是您的部署域名。
  • <token> 应该替换为您生成的秘密令牌。
  • <path> 应该是您要预览的页面的路径。如果您要预览 /posts/foo,则应使用 &slug=/posts/foo

您的无头 CMS 可能允许您在预览 URL 中包含一个变量,以便 <path> 可以根据 CMS 的数据动态设置,如下所示:&slug=/posts/{entry.fields.slug}

最后,在预览 API 路由中

  • 检查秘密是否匹配以及 slug 参数是否存在(如果不存在,请求应失败)。
  • 调用 res.setPreviewData
  • 然后将浏览器重定向到 slug 指定的路径。(以下示例使用 307 重定向)。
export default async (req, res) => {
  // Check the secret and next parameters
  // This secret should only be known to this API route and the CMS
  if (req.query.secret !== 'MY_SECRET_TOKEN' || !req.query.slug) {
    return res.status(401).json({ message: 'Invalid token' })
  }
 
  // Fetch the headless CMS to check if the provided `slug` exists
  // getPostBySlug would implement the required fetching logic to the headless CMS
  const post = await getPostBySlug(req.query.slug)
 
  // If the slug doesn't exist prevent preview mode from being enabled
  if (!post) {
    return res.status(401).json({ message: 'Invalid slug' })
  }
 
  // Enable Preview Mode by setting the cookies
  res.setPreviewData({})
 
  // Redirect to the path from the fetched post
  // We don't redirect to req.query.slug as that might lead to open redirect vulnerabilities
  res.redirect(post.slug)
}

如果成功,浏览器将重定向到您要预览的路径,并设置预览模式 cookie。

步骤 2:更新 getStaticProps

下一步是更新 getStaticProps 以支持预览模式。

如果您使用已设置预览模式 cookie(通过 res.setPreviewData)请求具有 getStaticProps 的页面,则 getStaticProps 将在请求时(而不是构建时)调用。

此外,它将使用一个 context 对象调用,其中

  • context.preview 将为 true
  • context.previewData 将与用于 setPreviewData 的参数相同。
export async function getStaticProps(context) {
  // If you request this page with the preview mode cookies set:
  //
  // - context.preview will be true
  // - context.previewData will be the same as
  //   the argument used for `setPreviewData`.
}

我们在预览 API 路由中使用了 res.setPreviewData({}),因此 context.previewData 将为 {}。如果需要,您可以使用它将会话信息从预览 API 路由传递到 getStaticProps

如果您还使用 getStaticPaths,那么 context.params 也将可用。

获取预览数据

您可以更新 getStaticProps 以根据 context.preview 和/或 context.previewData 获取不同的数据。

例如,您的无头 CMS 可能有不同的草稿文章 API 端点。如果是这样,您可以使用 context.preview 修改 API 端点 URL,如下所示

export async function getStaticProps(context) {
  // If context.preview is true, append "/preview" to the API endpoint
  // to request draft data instead of published data. This will vary
  // based on which headless CMS you're using.
  const res = await fetch(`https://.../${context.preview ? 'preview' : ''}`)
  // ...
}

就是这样!如果您从无头 CMS 或手动访问预览 API 路由(带 secretslug),您现在应该能够看到预览内容。如果您在未发布的情况下更新草稿,您应该能够预览草稿。

将其设置为您的无头 CMS 上的预览 URL 或手动访问,您应该能够看到预览。

终端
https://<your-site>/api/preview?secret=<token>&slug=<path>

更多详情

须知:在渲染过程中 next/router 公开了 isPreview 标志,更多信息请参见 路由对象文档

指定预览模式持续时间

setPreviewData 接受一个可选的第二个参数,它应该是一个选项对象。它接受以下键

  • maxAge:指定预览会话持续的秒数。
  • path:指定 cookie 应该应用的路径。默认为 /,为所有路径启用预览模式。
setPreviewData(data, {
  maxAge: 60 * 60, // The preview mode cookies expire in 1 hour
  path: '/about', // The preview mode cookies apply to paths with /about
})

清除预览模式 cookie

默认情况下,预览模式 cookie 没有设置过期日期,因此预览会话在浏览器关闭时结束。

要手动清除预览模式 cookie,请创建一个调用 clearPreviewData() 的 API 路由

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 中使其可用。但是,由于数据将存储在 cookie 中,因此存在大小限制。目前,预览数据限制为 2KB。

适用于 getServerSideProps

预览模式也适用于 getServerSideProps。它也将在包含 previewpreviewDatacontext 对象上可用。

须知:在使用预览模式时,不应设置 Cache-Control 标头,因为它无法绕过。我们建议改用 ISR

适用于 API 路由

API 路由将在请求对象下访问 previewpreviewData。例如

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

每个 next build 都是唯一的

next build 完成时,绕过 cookie 值和加密 previewData 的私钥都会更改。这确保了绕过 cookie 无法被猜测。

须知:要在本地通过 HTTP 测试预览模式,您的浏览器需要允许第三方 cookie 和本地存储访问。