跳至内容

预览模式

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

示例

页面文档数据获取文档中,我们讨论了如何使用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 会开启预览模式。任何包含这些 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。

从您的 Headless 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 中包含一个变量,以便可以根据 CMS 的数据动态设置 <path>,例如:&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以支持预览模式。

如果您请求一个具有getStaticProps的页面,并且设置了预览模式 Cookie(通过res.setPreviewData),则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唯一

绕过 Cookie 值和用于加密previewData的私钥都会在next build完成后发生更改。这确保了无法猜测绕过 Cookie。

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