跳到內容
頁面路由指南預覽模式

如何在 Next.js 中使用預覽模式預覽內容

這是一箇舊版 API,不再推薦使用。為了向後相容性,它仍然受支援。

注意:此功能已被 草稿模式 取代。

示例

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

當您的頁面從無頭 CMS 獲取資料時,靜態生成非常有用。但是,當您在無頭 CMS 上編寫草稿並希望在頁面上立即預覽草稿時,它並不理想。您希望 Next.js 在請求時而不是構建時渲染這些頁面,並獲取草稿內容而不是已釋出內容。您希望 Next.js 僅在這種特定情況下繞過靜態生成。

Next.js 有一個名為預覽模式的功能,可以解決此問題。以下是使用說明。

步驟 1:建立並訪問預覽 API 路由

如果您不熟悉 Next.js API 路由,請首先檢視 API 路由文件

首先,建立一個預覽 API 路由。它可以有任何名稱,例如 pages/api/preview.js(如果使用 TypeScript,則為 .ts)。

在此 API 路由中,您需要在響應物件上呼叫 setPreviewDatasetPreviewData 的引數應該是一個物件,getStaticProps 可以使用它(稍後會詳細介紹)。現在,我們使用 {}

export default function handler(req, res) {
  // ...
  res.setPreviewData({})
  // ...
}

res.setPreviewData 在瀏覽器上設定了一些 cookie,從而開啟了預覽模式。任何包含這些 cookie 的 Next.js 請求都將被視為預覽模式,靜態生成頁面的行為將發生變化(稍後會詳細介紹)。

您可以透過建立如下所示的 API 路由並手動從瀏覽器訪問它來手動測試

pages/api/preview.js
// 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。

從無頭 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 中包含一個變數,以便 <path> 可以根據 CMS 的資料動態設定,如下所示:&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 以支援預覽模式。

如果您使用已設定預覽模式 cookie(透過 res.setPreviewData)請求具有 getStaticProps 的頁面,則 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 路由(帶 secretslug),您現在應該能夠看到預覽內容。如果您在未釋出的情況下更新草稿,您應該能夠預覽草稿。

將其設定為您的無頭 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 路由

pages/api/clear-preview-mode-cookies.js
export default function handler(req, res) {
  res.clearPreviewData({})
}

然後,向 /api/clear-preview-mode-cookies 傳送請求以呼叫 API 路由。如果使用 next/link 呼叫此路由,則必須傳入 prefetch={false} 以防止在連結預取期間呼叫 clearPreviewData

如果在 setPreviewData 呼叫中指定了路徑,則必須將相同的路徑傳遞給 clearPreviewData

pages/api/clear-preview-mode-cookies.js
export default function handler(req, res) {
  const { path } = req.query
 
  res.clearPreviewData({ path })
}

previewData 大小限制

您可以將一個物件傳遞給 setPreviewData,並在 getStaticProps 中使其可用。但是,由於資料將儲存在 cookie 中,因此存在大小限制。目前,預覽資料限制為 2KB。

適用於 getServerSideProps

預覽模式也適用於 getServerSideProps。它也將在包含 previewpreviewDatacontext 物件上可用。

須知:在使用預覽模式時,不應設定 Cache-Control 標頭,因為它無法繞過。我們建議改用 ISR

適用於 API 路由

API 路由將在請求物件下訪問 previewpreviewData。例如

export default function myApiRoute(req, res) {
  const isPreview = req.preview
  const previewData = req.previewData
  // ...
}

每個 next build 都是唯一的

next build 完成時,繞過 cookie 值和加密 previewData 的私鑰都會更改。這確保了繞過 cookie 無法被猜測。

須知:要在本地透過 HTTP 測試預覽模式,您的瀏覽器需要允許第三方 cookie 和本地儲存訪問。