跳到主要内容

草稿模式

在从无头 CMS 获取数据的页面中,静态渲染非常有用。 然而,当您在无头 CMS 上编写草稿并希望立即在页面上查看草稿时,静态渲染并不理想。 您希望 Next.js 在请求时而不是构建时渲染这些页面,并获取草稿内容而不是已发布的内容。 您希望 Next.js 仅在这种特定情况下切换到动态渲染。

Next.js 有一个名为“草稿模式(Draft Mode)”的功能,用于解决这个问题。 以下是如何使用它的说明。

步骤 1:创建并访问路由处理程序​

首先,创建一个路由处理程序。 它可以有任何名称,例如 app/api/draft/route.ts

然后,从 next/headers 中导入 draftMode 并调用 enable() 方法。

app/api/draft/route.ts
// route handler enabling draft mode
import { draftMode } from 'next/headers'

export async function GET(request: Request) {
draftMode().enable()
return new Response('Draft mode is enabled')
}

这将设置一个 cookie 以启用草稿模式。 随后包含此 cookie 的请求将触发 Draft Mode, 更改静态生成页面的行为(稍后详细说明)。

您可以通过访问 /api/draft 并查看浏览器的开发者工具来手动测试这一点。 注意 Set-Cookie 响应头带有一个名为 __prerender_bypass 的 cookie。

安全访问来自无头 CMS 的草稿模式​

在实际应用中,您可能希望从无头 CMS 安全地调用此路由处理程序。 具体步骤将取决于您使用的无头 CMS,但以下是您可能采取的一些常见步骤。

这些步骤假定您使用的无头 CMS 支持设置自定义草稿 URL。 如果不支持,您仍然可以使用此方法保护您的草稿 URL,但需要手动构建和访问草稿 URL。

首先,您应使用所选的令牌生成器创建一个秘密令牌字符串。 这个秘密只会被您的 Next.js 应用程序和无头 CMS 知道。 这个秘密可以防止没有访问您的 CMS 权限的人访问草稿 URL。

其次,如果您的无头 CMS 支持设置自定义草稿 URL,请将以下内容指定为草稿 URL。 这假设您的路由处理程序位于 app/api/draft/route.ts

https://<your-site>/api/draft?secret=<token>&slug=<path>
  • <your-site> 应该是您的部署域。
  • <token> 应该替换为您生成的秘密令牌。
  • <path> 应该是您想要查看的页面的路径。 如果要查看 /posts/foo,则应使用 &slug=/posts/foo。
  • 您的无头 CMS 可能允许您在草稿 URL 中包含变量, 以便可以基于 CMS 数据动态设置 <path>,例如:&slug=/posts/{entry.fields.slug}

最后,在路由处理程序中:

  • 检查秘密是否匹配,以及是否存在 slug 参数(如果不存在,则请求应失败)。
  • 调用 draftMode.enable() 来设置 cookie。
  • 然后将浏览器重定向到 slug 指定的路径。
app/api/draft/route.ts
// route handler with secret and slug
import { draftMode } from 'next/headers'
import { redirect } from 'next/navigation'

export async function GET(request: Request) {
// 解析查询字符串参数
const { searchParams } = new URL(request.url)
const secret = searchParams.get('secret')
const slug = searchParams.get('slug')

// 检查秘密和 next 参数
// 此秘密只应为此路由处理程序和 CMS 所知
if (secret !== 'MY_SECRET_TOKEN' || !slug) {
return new Response('Invalid token', { status: 401 })
}

// 获取无头 CMS 以检查提供的 `slug` 是否存在
// getPostBySlug 应实现到无头 CMS 的所需获取逻辑
const post = await getPostBySlug(slug)

// 如果 slug 不存在,请防止启用草稿模式
if (!post) {
return new Response('Invalid slug', { status: 401 })
}

// 通过设置 cookie 启用草稿模式
draftMode().enable()

// 重定向到从获取的文章中获取的路径
// 我们不重定向到 searchParams.slug,因为这可能导致开放重定向漏洞
redirect(post.slug)
}

如果成功,浏览器将被重定向到使用草稿模式 cookie 查看的路径。

步骤 2:更新页面​

下一步是更新您的页面以检查 draftMode().isEnabled 的值。

如果请求的页面已设置了 cookie,则将在请求时(而不是在构建时)获取数据。

此外,isEnabled 的值将为 true。

app/page.tsx
// page that fetches data
import { draftMode } from 'next/headers'

async function getData() {
const { isEnabled } = draftMode()

const url = isEnabled
? 'https://draft.example.com'
: 'https://production.example.com'

const res = await fetch(url)

return res.json()
}

export default async function Page() {
const { title, desc } = await getData()

return (
<main>
<h1>{title}</h1>
<p>{desc}</p>
</main>
)
}

就是这样!如果您从无头 CMS 或手动访问草稿路由处理程序(带有secret和 slug), 您现在应该能够看到草稿内容。如果您更新了草稿但尚未发布,您应该能够查看草稿。

将其设置为无头 CMS 上的草稿 URL 或手动访问,您应该能够看到草稿。

https://<your-site>/api/draft?secret=<token>&slug=<path>

更多细节​

默认情况下,草稿模式会在关闭浏览器时结束会话。

要手动清除草稿模式 cookie,请创建一个调用 draftMode().disable() 的路由处理程序:

app/api/disable-draft/route.ts
// route handler with secret and slug
import { draftMode } from 'next/headers'

export async function GET(request: Request) {
draftMode().disable()
return new Response('Draft mode is disabled')
}

然后,发送请求到 /api/disable-draft 以调用路由处理程序。 如果使用 next/link 调用此路由,必须传递 prefetch={false} 以防止意外删除预取时的 cookie。

每次 next build 时唯一的​

每次运行 next build 时,都会生成一个新的绕过 cookie 值。

这确保了无法猜测绕过 cookie。

备注

要在本地通过 HTTP 测试草稿模式,您的浏览器将需要允许第三方 cookie 和本地存储访问。