草稿模式

頁面文件資料獲取文件中,我們討論了如何使用 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,其 cookie 名為 __prerender_bypass

從無頭 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 中包含變數,使 <path> 能根據 CMS 資料動態設定,例如:&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。

步驟 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