预览模式
注意:此功能已被草稿模式取代。
示例
- Agility CMS 示例(演示)
- Builder.io 示例(演示)
- ButterCMS 示例(演示)
- Contentful 示例(演示)
- Cosmic 示例(演示)
- DatoCMS 示例(演示)
- DotCMS 示例(演示)
- Drupal 示例(演示)
- Enterspeed 示例(演示)
- GraphCMS 示例(演示)
- Keystone 示例(演示)
- Kontent.ai 示例(演示)
- Makeswift 示例(演示)
- Plasmic 示例(演示)
- Prepr 示例(演示)
- Prismic 示例(演示)
- Sanity 示例(演示)
- Sitecore XM Cloud 示例(演示)
- Storyblok 示例(演示)
- Strapi 示例(演示)
- TakeShape 示例(演示)
- Tina 示例(演示)
- Umbraco 示例(演示)
- Umbraco Heartcore 示例(演示)
- Webiny 示例(演示)
- WordPress 示例(演示)
- 博客启动器示例(演示)
在Pages 文档和数据获取文档中,我们讨论了如何在构建时预渲染页面(静态生成),使用 getStaticProps
和 getStaticPaths
。
当你的页面从 Headless CMS 获取数据时,静态生成非常有用。但是,当你在 Headless CMS 上编写草稿并希望立即在页面上预览草稿时,它并不理想。你希望 Next.js 在请求时而不是构建时渲染这些页面,并获取草稿内容而不是已发布的内容。你希望 Next.js 仅在这种特定情况下绕过静态生成。
Next.js 有一个名为预览模式的功能,可以解决这个问题。以下是如何使用它的说明。
步骤 1:创建并访问预览 API 路由
如果您不熟悉 Next.js API 路由,请先查看 API 路由文档。
首先,创建一个 预览 API 路由。它可以有任何名称 - 例如 pages/api/preview.js
(如果使用 TypeScript,则为 .ts
)。
在这个 API 路由中,您需要在响应对象上调用 setPreviewData
。setPreviewData
的参数应该是一个对象,它可以被 getStaticProps
使用(稍后会详细介绍)。现在,我们将使用 {}
。
export default function handler(req, res) {
// ...
res.setPreviewData({})
// ...
}
res.setPreviewData
在浏览器上设置一些 cookies,从而开启预览模式。任何包含这些 cookies 的 Next.js 请求都将被视为 预览模式,并且静态生成页面的行为将发生改变(稍后会详细介绍)。
您可以通过手动创建一个如下所示的 API 路由,并从浏览器手动访问它来测试这一点
// 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
cookies 将会在这个请求中被设置。
从您的 Headless CMS 安全地访问它
在实践中,您会希望从您的 headless CMS 安全地 调用这个 API 路由。具体的步骤会因您使用的 headless CMS 而异,但以下是一些您可以采取的常见步骤。
这些步骤假设您使用的 headless CMS 支持设置 自定义预览 URL。如果不支持,您仍然可以使用此方法来保护您的预览 URL,但您需要手动构建和访问预览 URL。
首先,您应该使用您选择的令牌生成器创建一个 密钥令牌字符串。这个密钥将只被您的 Next.js 应用程序和您的 headless CMS 知道。这个密钥可以防止没有访问您的 CMS 权限的人访问预览 URL。
其次,如果您的 headless 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
。
您的 headless 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)
}
如果成功,浏览器将被重定向到您想要预览的路径,并设置预览模式 cookies。
步骤 2:更新 getStaticProps
下一步是更新 getStaticProps
以支持预览模式。
如果您请求一个页面,该页面具有设置了预览模式 cookies 的 getStaticProps
(通过 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
获取不同的数据。
例如,您的 headless 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' : ''}`)
// ...
}
就是这样!如果您从您的 headless CMS 或手动访问预览 API 路由(带有 secret
和 slug
),您现在应该能够看到预览内容。如果您更新草稿但未发布,您应该能够预览草稿。
在您的 headless 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
})
清除预览模式 cookies
默认情况下,预览模式 cookies 未设置过期日期,因此预览会话在浏览器关闭时结束。
要手动清除预览模式 cookies,创建一个调用 clearPreviewData()
的 API 路由
export default function handler(req, res) {
res.clearPreviewData({})
}
然后,发送一个请求到 /api/clear-preview-mode-cookies
以调用 API 路由。如果使用 next/link
调用此路由,您必须传递 prefetch={false}
以防止在链接预取期间调用 clearPreviewData
。
如果在 setPreviewData
调用中指定了路径,您必须将相同的路径传递给 clearPreviewData
export default function handler(req, res) {
const { path } = req.query
res.clearPreviewData({ path })
}
previewData
大小限制
您可以传递一个对象给 setPreviewData
,并使其在 getStaticProps
中可用。但是,由于数据将存储在 cookie 中,因此存在大小限制。目前,预览数据限制为 2KB。
与 getServerSideProps
一起使用
预览模式也适用于 getServerSideProps
。它也将在包含 preview
和 previewData
的 context
对象上可用。
须知:使用预览模式时,您不应设置
Cache-Control
标头,因为它无法被绕过。相反,我们建议使用 ISR。
与 API 路由一起使用
API 路由将可以访问请求对象下的 preview
和 previewData
。例如
export default function myApiRoute(req, res) {
const isPreview = req.preview
const previewData = req.previewData
// ...
}
每次 next build
都是唯一的
当 next build
完成时,绕过 cookie 值和用于加密 previewData
的私钥都会更改。这确保了绕过 cookie 不会被猜到。
须知:要在本地通过 HTTP 测试预览模式,您的浏览器需要允许第三方 cookies 和本地存储访问。
这是否有帮助?