在页面文档 中,我们说明了如何在构建过程中使用
getStaticProps
和
getStaticPaths
来预渲染一个页面的方法。
当你的页面从一个 Headless CMS 中获取数据的时候,“静态生成”会很有用。然而, 这在当你想要在你的 Headless CMS 页面中撰写草稿并能立即预览草稿的场景下 并不理想。你会希望 Blitz 在请求过程而非构建过程渲染这些页面,同时获取 草稿的内容而非已发布的内容。你会希望 Blitz 能在这种特殊情况下绕过“静态生成 ”。
Blitz 提供预览模式功能来解决这个问题。接下来是如何使用的操作说明。
如果你还不熟悉 Blitz 的 API 路由,请先阅读 API 路由文档。
首先,创建一个预览 API 路由。其可以是任何名称,例如
app/api/preview.js
(或在你使用 TypeScript 时用 .ts
)。
在这个 API 路由中,你需要在响应对象中调用
setPreviewData
。setPreviewData
的参数需要是一个对象,且可以被
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 中 安全地 访问这个 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 来被重定向到你想要预览 的路径。
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 路由(携带
secret
和 slug
),你现在可以看到预览的内容了。如果你在不发布的状态下更
新了你的草稿,你将能够预览你的草稿。
# 设置如下为你的 Headless CMS 预览 URL 或手动访问,然后你将能够进行预览。
https://<your-site>/api/preview?secret=<token>&slug=<path>
默认情况下,预览模式的 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
上配合使用。它也将可在包含 preview
和 previewData
的 context
对象上使用。
API 路由将有权访问请求对象下的 preview
和 previewData
。例如:
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 和本地存储访问权。