如何在 Next.js 中使用草稿模式预览内容
在页面文档和数据获取文档中,我们讨论了如何使用 getStaticProps
和 getStaticPaths
在构建时预渲染页面(静态生成)。
当页面从无头 CMS 获取数据时,静态生成非常有用。但如果您正在无头 CMS 上撰写草稿并希望立即在页面上查看,这种方式就不理想了。此时您会希望 Next.js 在请求时而非构建时渲染这些页面,并获取草稿内容而非已发布内容。您需要 Next.js 仅在此特定情况下绕过静态生成。
Next.js 的草稿模式 (Draft Mode) 功能正是为此而生。以下是使用指南。
第一步:创建并访问 API 路由
如果您不熟悉 Next.js API 路由,请先查阅 API 路由文档。
首先创建 API 路由,可任意命名(例如 pages/api/draft.ts
)。
在此 API 路由中,需在响应对象上调用 setDraftMode
。
export default function handler(req, res) {
// ...
res.setDraftMode({ enable: true })
// ...
}
这将设置一个Cookie来启用草稿模式。后续携带此 Cookie 的请求将触发草稿模式,从而改变静态生成页面的行为(下文详述)。
您可以通过手动创建如下 API 路由并从浏览器访问来测试:
// 用于从浏览器手动测试的简单示例
export default function handler(req, res) {
res.setDraftMode({ enable: true })
res.end('草稿模式已启用')
}
如果在浏览器开发者工具中访问 /api/draft
,会注意到响应头中有 Set-Cookie
字段,其值为名为 __prerender_bypass
的 Cookie。
从无头 CMS 安全访问
实际应用中,您需要从无头 CMS安全地调用此 API 路由。具体步骤因 CMS 而异,但以下是通用方案:
此方案假设您的无头 CMS 支持设置自定义草稿 URL。若不支持,您仍可用此方法保护草稿 URL,但需手动构建和访问。
首先,使用任意令牌生成器创建密钥字符串。该密钥仅由 Next.js 应用和无头 CMS 知晓,可防止无 CMS 访问权限者获取草稿 URL。
其次,若 CMS 支持自定义草稿 URL,请将以下内容设为草稿 URL(假设草稿 API 路由位于 pages/api/draft.ts
):
https://<your-site>/api/draft?secret=<token>&slug=<path>
<your-site>
替换为部署域名<token>
替换为生成的密钥<path>
替换为目标页面路径(如欲查看/posts/foo
,则使用&slug=/posts/foo
)
某些 CMS 允许在草稿 URL 中包含变量,从而动态设置 <path>
,例如:&slug=/posts/{entry.fields.slug}
最后,在草稿 API 路由中:
- 检查密钥是否匹配及
slug
参数是否存在(否则请求应失败) - 调用
res.setDraftMode
- 重定向浏览器到
slug
指定路径(下例使用 307 重定向)
export default async (req, res) => {
// 验证密钥和必要参数
// 此密钥应仅限此API路由和CMS知晓
if (req.query.secret !== 'MY_SECRET_TOKEN' || !req.query.slug) {
return res.status(401).json({ message: '无效令牌' })
}
// 向无头CMS查询该slug是否存在
// getPostBySlug需实现针对无头CMS的获取逻辑
const post = await getPostBySlug(req.query.slug)
// 若slug不存在则阻止启用草稿模式
if (!post) {
return res.status(401).json({ message: '无效slug' })
}
// 通过设置Cookie启用草稿模式
res.setDraftMode({ enable: true })
// 重定向到获取的文章路径
// 不直接重定向到req.query.slug以防开放重定向漏洞
res.redirect(post.slug)
}
成功后,浏览器将携带草稿模式 Cookie 重定向到目标路径。
第二步:更新 getStaticProps
接下来需更新 getStaticProps
以支持草稿模式。
若请求的页面具有 getStaticProps
且已设置 Cookie(通过 res.setDraftMode
),则 getStaticProps
将在请求时被调用(而非构建时)。
此外,其调用时将接收 context
对象,其中 context.draftMode
为 true
。
export async function getStaticProps(context) {
if (context.draftMode) {
// 动态数据
}
}
由于我们在草稿 API 路由中使用了 res.setDraftMode
,故 context.draftMode
将为 true
。
若同时使用 getStaticPaths
,则 context.params
也可用。
获取草稿数据
可根据 context.draftMode
修改 getStaticProps
以获取不同数据。
例如,无头 CMS 可能有专用的草稿文章 API 端点。此时可如下修改端点 URL:
export async function getStaticProps(context) {
const url = context.draftMode
? 'https://draft.example.com'
: 'https://production.example.com'
const res = await fetch(url)
// ...
}
完成!现在从无头 CMS 或手动访问草稿 API 路由(携带 secret
和 slug
)即可查看草稿内容。当您更新草稿但未发布时,也能立即查看。
将此 URL 设为无头 CMS 的草稿 URL 或手动访问:
https://<your-site>/api/draft?secret=<token>&slug=<path>
更多细节
清除草稿模式 Cookie
默认情况下,草稿模式会话在浏览器关闭时结束。
要手动清除 Cookie,可创建调用 setDraftMode({ enable: false })
的 API 路由:
export default function handler(req, res) {
res.setDraftMode({ enable: false })
}
然后向 /api/disable-draft
发送请求。若使用 next/link
调用此路由,必须传递 prefetch={false}
以防预取时意外删除 Cookie。
与 getServerSideProps
协作
草稿模式可与 getServerSideProps
协同工作,在上下文对象中以 draftMode
键存在。
须知:使用草稿模式时不应设置
Cache-Control
标头,因其无法被绕过。建议改用 ISR。
与 API 路由协作
API 路由可通过请求对象的 draftMode
访问该状态。例如:
export default function myApiRoute(req, res) {
if (req.draftMode) {
// 获取草稿数据
}
}
每次 next build
生成唯一值
每次运行 next build
都会生成新的绕过 Cookie 值。
这确保了绕过 Cookie 无法被猜测。
须知:要通过 HTTP 在本地测试草稿模式,浏览器需允许第三方 Cookie 和本地存储访问。