草稿模式

頁面文件資料獲取文件中,我們討論了如何使用 getStaticPropsgetStaticPaths 在建置時預渲染頁面(靜態生成)。

當您的頁面從無頭 CMS 獲取資料時,靜態生成非常有用。然而,當您在無頭 CMS 上撰寫草稿並希望立即在頁面上預覽時,這種方式就不理想了。您會希望 Next.js 在請求時渲染這些頁面,而非建置時,並獲取草稿內容而非已發布的內容。您會希望 Next.js 僅在此特定情況下繞過靜態生成。

Next.js 的草稿模式功能正是為解決此問題而生。以下是使用說明。

步驟 1:建立並存取 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 路由並從瀏覽器手動存取:

pages/api/draft.ts
// 簡單範例,供您從瀏覽器手動測試
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,但需手動建構並存取草稿 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 中包含變數,以便根據 CMS 的資料動態設定 <path>,例如:&slug=/posts/{entry.fields.slug}

最後,在草稿 API 路由中:

  • 檢查密鑰是否匹配及 slug 參數是否存在(若無,請求應失敗)。
  • 呼叫 res.setDraftMode
  • 然後將瀏覽器重新導向至 slug 指定的路徑(以下範例使用 307 重新導向)。
export default async (req, res) => {
  // 檢查密鑰與 slug 參數
  // 此密鑰應僅此 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。

步驟 2:更新 getStaticProps

下一步是更新 getStaticProps 以支援草稿模式。

若您請求的頁面具有 getStaticProps 且已設定 cookie(透過 res.setDraftMode),則 getStaticProps 將在請求時被呼叫(而非建置時)。

此外,其將被呼叫並接收一個 context 物件,其中 context.draftModetrue

export async function getStaticProps(context) {
  if (context.draftMode) {
    // 動態資料
  }
}

我們已在草稿 API 路由中使用 res.setDraftMode,因此 context.draftMode 將為 true

若您同時使用 getStaticPaths,則 context.params 也將可用。

獲取草稿資料

您可更新 getStaticProps,根據 context.draftMode 獲取不同資料。

例如,您的無頭 CMS 可能有專用的草稿文章 API 端點。若是如此,您可如下修改 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 路由(附帶 secretslug),現在應能檢視草稿內容。若您更新草稿但未發布,您應能預覽草稿。

將此設為無頭 CMS 的草稿 URL 或手動存取,您應能檢視草稿。

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

更多細節

預設情況下,草稿模式工作階段將於瀏覽器關閉時結束。

若要手動清除草稿模式 cookie,請建立一個呼叫 setDraftMode({ enable: false }) 的 API 路由:

pages/api/disable-draft.ts
export default function handler(req, res) {
  res.setDraftMode({ enable: false })
}

然後,傳送請求至 /api/disable-draft 以呼叫此 API 路由。若使用 next/link 呼叫此路由,必須傳遞 prefetch={false} 以避免在預取時意外刪除 cookie。

getServerSideProps 協同工作

草稿模式可與 getServerSideProps 協同工作,並作為 draftMode 鍵存在於 context 物件中。

須知:使用草稿模式時不應設定 Cache-Control 標頭,因其無法被繞過。我們建議改用 ISR

與 API 路由協同工作

API 路由將可透過請求物件存取 draftMode。例如:

export default function myApiRoute(req, res) {
  if (req.draftMode) {
    // 獲取草稿資料
  }
}

每次 next build 生成唯一值

每次執行 next build 時,將生成新的繞過 cookie 值。

此機制確保繞過 cookie 無法被猜測。

須知:若要在 HTTP 環境下本地測試草稿模式,您的瀏覽器需允許第三方 cookie 與本地儲存存取。

On this page