Blitz 尚在 beat 阶段! 🎉 预计会在今年的 Q3 季度发布 1.0
Back to Documentation Menu

预览模式

Topics

Jump to a Topic

页面文档 中,我们说明了如何在构建过程中使用 getStaticPropsgetStaticPaths 来预渲染一个页面的方法。

当你的页面从一个 Headless CMS 中获取数据的时候,“静态生成”会很有用。然而, 这在当你想要在你的 Headless CMS 页面中撰写草稿并能立即预览草稿的场景下 并不理想。你会希望 Blitz 在请求过程而非构建过程渲染这些页面,同时获取 草稿的内容而非已发布的内容。你会希望 Blitz 能在这种特殊情况下绕过“静态生成 ”。

Blitz 提供预览模式功能来解决这个问题。接下来是如何使用的操作说明。

步骤 1:创建和访问一个预览 API 路由

如果你还不熟悉 Blitz 的 API 路由,请先阅读 API 路由文档

首先,创建一个预览 API 路由。其可以是任何名称,例如 app/api/preview.js(或在你使用 TypeScript 时用 .ts)。

在这个 API 路由中,你需要在响应对象中调用 setPreviewDatasetPreviewData 的参数需要是一个对象,且可以被 getStaticProps 使用(稍后深入说明)。目前我们先使用 {}

const handler = (req, res) => {
  // ...
  res.setPreviewData({})
  // ...
}
export default handler

res.setPreviewData 为浏览器设置了一些可以用来进入预览模式的 cookies。任何包含这些 cookies 的请求传递给 Blitz,将会被视为预览模 式,此时静态生成文件的行为将会被改变(稍后深入说明)。

你可以通过创建一个像如下的 API 路由来手动测试,并在你的浏览器中访问:

// 一个简单的示例来手动在你的浏览器中测试。
// 如果当前位置是 app/api/preview.js,则打开在浏览器中打开 /api/preview。
const handler = (req, res) => {
  res.setPreviewData({})
  res.end("Preview mode enabled")
}
export default handler

如果你在你的浏览器中使用了开发者工具,你这时可以看到 __prerender_bypass__next_preview_data cookies 已被附加到请求体中。

从你的 Headless CMS 中安全地访问

实践中,你希望从 Headless CMS 中 安全地 访问这个 API 路由。具体的步骤会 因你所使用的 Headless CMS 而有所不同。但如下包含你可以采取的一些常见步骤。

这些步骤假设你使用到的 Headless CMS 支持设置自定义预览网址。如果不支持 ,你依然可以使用这些方法来保护预览网址,但你需要手动构造和访问预览网址。

首先,你需要使用 Token 生成器创建一个私密 Token 字符串,且其仅能被 你的 Blitz 应用和你的 Headless CMS 识别。该私密方式可防止无权访问你的 CMS 的人来访问预览网址。

其次,如果你的 Headless CMS 支持设置自定义预览网址,指定以下内容作为预 览 URL。(此时假设你的预览 API 路由位于 app/api/preview.js)。

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

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

最终,在预览 API 路由中:

  • 检查秘钥是否匹配,且当前 slug 参数是否存在(如果不存在,则请求将会失败 )。
  • 调用 res.setPreviewData
  • 然后将浏览器重定向到 slug 指定的路径。(以下示例使用 307 redirect) 。
export default async (req, res) => {
  // 检查秘钥和下一个参数。
  // 这个秘钥只允许被当前 API 路由和 CMS 读取。
  if (req.query.secret !== "MY_SECRET_TOKEN" || !req.query.slug) {
    return res.status(401).json({ message: "Invalid token" })
  }

  // 请求 Headless CMS 来检查提供的 `slug` 是否存在
  // getPostBySlug 将为 Headless CMS 实现所需的提取逻辑
  const post = await getPostBySlug(req.query.slug)

  // 如果 slug 不存在,将阻止预览模式被启用
  if (!post) {
    return res.status(401).json({ message: "Invalid slug" })
  }

  // 通过设置 cookies 来开启预览模式
  res.setPreviewData({})

  // 从获取到的文章重定向到相关路径
  // 我们不会直接重定向到 req.query.slug,因为这可能会导致重定向安全性漏洞
  res.writeHead(307, { Location: post.slug })
  res.end()
}

如果一切成功,则浏览器将携带被设置的预览模式 Cookie 来被重定向到你想要预览 的路径。

步骤 2:更新 getStaticProps

接下来的一步是更新 getStaticProps 来支持预览模式。

如果你请求的页面具有通过预览模式 Cookie 设置(res.setPreviewData)的 getStaticProps,则 getStaticProps 将在请求过程而非构建过程中被调用 。

此外,将使用 context 对象来调用该对象,其中:

  • context.preview 值将 true
  • context.previewData 将与用于 setPreviewData 的参数相同。
export async function getStaticProps(context) {
  // 如果你通过预览模式 Cookie请求此页面:
  //
  // - context.preview 将会为 true
  // - `context.previewData` 将与用于 `setPreviewData` 的参数相同。
}

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

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

获取预览数据

你可以更新 getStaticProps 来基于 context.preview 和/或 context.previewData 来获取不同的数据。

例如,你的 Headless CMS 可能针对草稿文章有一个不同的 API 端口。如果是这样 ,你可以使用 context.preview 来像如下修改 API 端口 URL:

export async function getStaticProps(context) {
  // 如果 context.preview 是 true,则将“/preview”添加到 API 端口上
  // 来获取草稿数据而非已发布的数据。这里根据你使用的 Headless CMS 会有所不同。
  const res = await fetch(
    `https://.../${context.preview ? "preview" : ""}`
  )
  // ...
}

就是这样!如果你手动或者通过 Headless CMS 来访问这个预览 API 路由(携带 secretslug),你现在可以看到预览的内容了。如果你在不发布的状态下更 新了你的草稿,你将能够预览你的草稿。

# 设置如下为你的 Headless CMS 预览 URL 或手动访问,然后你将能够进行预览。
https://<your-site>/api/preview?secret=<token>&slug=<path>

更多细节

清空预览模式的 Cookies

默认情况下,预览模式的 Cookie 是没有设置过期时间的,因此关闭浏览器时预览模 式也将结束。

要想手动清除预览 Cookies,你可以创建一个叫作 clearPreviewData 的 API 路 由然后访问这个路由。

export default (req, res) => {
  // 清除预览模式 Cookies。
  // 该函数不接收参数。
  res.clearPreviewData()
  // ...
}

指定预览模式的持续时间

setPreviewData 具有第二个可选参数,该参数应为 options 对象。它接收如下键 :

  • maxAge:指定预览 session 要持续的数字(以秒为单位)。
setPreviewData(data, {
  maxAge: 60 * 60, // 预览模式 Cookie 过期时间为 1 小时
})

previewData 大小限制

你可以将一个对象传递给 setPreviewData,并使其在 getStaticProps 中可用 。但是,由于数据将存储在 Cookie 中,因此存在大小限制。当前预览数据限制在 2KB 下。

getServerSideProps 配合使用

预览模式也可以在 getServerSideProps 上配合使用。它也将可在包含 previewpreviewDatacontext 对象上使用。

与 API 路由配合使用

API 路由将有权访问请求对象下的 previewpreviewData。例如:

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

每次唯一的 blitz build

每当运行 blitz build 时,用于加密 previewData 的旁路 cookie(bypass cookie)和秘钥都会改变,从而确保旁路 Cookie 不会被猜测到。

注意: 想要通过 HTTP 在本地测试预览模式,你的浏览器将需要允许第三方 Cookie 和本地存储访问权。


Idea for improving this page? Edit it on GitHub.