预览模式
注意:此功能已被 草稿模式 取代。
示例
- WordPress 示例 (演示)
- DatoCMS 示例 (演示)
- TakeShape 示例 (演示)
- Sanity 示例 (演示)
- Prismic 示例 (演示)
- Contentful 示例 (演示)
- Strapi 示例 (演示)
- Prepr 示例 (演示)
- Agility CMS 示例 (演示)
- Cosmic 示例 (演示)
- ButterCMS 示例 (演示)
- Storyblok 示例 (演示)
- GraphCMS 示例 (演示)
- Kontent 示例 (演示)
- Umbraco Heartcore 示例 (演示)
- Plasmic 示例 (演示)
- Enterspeed 示例 (演示)
- Makeswift 示例 (演示)
在页面文档和数据获取文档中,我们讨论了如何使用getStaticProps
和getStaticPaths
在构建时预渲染页面(**静态生成**)。
当您的页面从无头 CMS 获取数据时,静态生成非常有用。但是,当您在无头 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
在浏览器上设置了一些**cookie**,这些 cookie 会开启预览模式。任何包含这些 cookie 的对 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
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 路由(带有secret
和slug
),您现在应该能够看到预览内容。如果您更新草稿而不发布,您应该能够预览草稿。
将此设置为无头 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 路由
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
唯一
绕过 Cookie 值和用于加密previewData
的私钥都会在next build
完成后发生更改。这确保了无法猜测绕过 Cookie。
需要了解:要在本地通过 HTTP 测试预览模式,您的浏览器需要允许第三方 Cookie 和本地存储访问。
这是否有帮助?