如何在 Next.js 中使用預覽模式預覽內容
注意:此功能已被 草稿模式 取代。
示例
- Agility CMS 示例 (演示)
- Builder.io 示例 (演示)
- ButterCMS 示例 (演示)
- Contentful 示例 (演示)
- Cosmic 示例 (演示)
- DatoCMS 示例 (演示)
- DotCMS 示例 (演示)
- Drupal 示例 (演示)
- Enterspeed 示例 (演示)
- GraphCMS 示例 (演示)
- Keystone 示例 (演示)
- Kontent.ai 示例 (演示)
- Makeswift 示例 (演示)
- Plasmic 示例 (演示)
- Prepr 示例 (演示)
- Prismic 示例 (演示)
- Sanity 示例 (演示)
- Sitecore XM Cloud 示例 (演示)
- Storyblok 示例 (演示)
- Strapi 示例 (演示)
- TakeShape 示例 (演示)
- Tina 示例 (演示)
- Umbraco 示例 (演示)
- Umbraco Heartcore 示例 (演示)
- Webiny 示例 (演示)
- WordPress 示例 (演示)
- 部落格入門示例 (演示)
在 頁面文件 和 資料獲取文件 中,我們討論瞭如何使用 getStaticProps 和 getStaticPaths 在構建時預渲染頁面(靜態生成)。
當您的頁面從無頭 CMS 獲取資料時,靜態生成非常有用。但是,當您在無頭 CMS 上編寫草稿並希望在頁面上立即預覽草稿時,它並不理想。您希望 Next.js 在請求時而不是構建時渲染這些頁面,並獲取草稿內容而不是已釋出內容。您希望 Next.js 僅在這種特定情況下繞過靜態生成。
Next.js 有一個名為預覽模式的功能,可以解決此問題。以下是使用說明。
步驟 1:建立並訪問預覽 API 路由
如果您不熟悉 Next.js API 路由,請首先檢視 API 路由文件。
首先,建立一個預覽 API 路由。它可以有任何名稱,例如 pages/api/preview.js(如果使用 TypeScript,則為 .ts)。
在此 API 路由中,您需要在響應物件上呼叫 setPreviewData。setPreviewData 的引數應該是一個物件,getStaticProps 可以使用它(稍後會詳細介紹)。現在,我們使用 {}。
export default function handler(req, res) {
// ...
res.setPreviewData({})
// ...
}res.setPreviewData 在瀏覽器上設定了一些 cookie,從而開啟了預覽模式。任何包含這些 cookie 的 Next.js 請求都將被視為預覽模式,靜態生成頁面的行為將發生變化(稍後會詳細介紹)。
您可以透過建立如下所示的 API 路由並手動從瀏覽器訪問它來手動測試
// 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 路由(帶 secret 和 slug),您現在應該能夠看到預覽內容。如果您在未釋出的情況下更新草稿,您應該能夠預覽草稿。
將其設定為您的無頭 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 路由
export default function handler(req, res) {
res.clearPreviewData({})
}然後,向 /api/clear-preview-mode-cookies 傳送請求以呼叫 API 路由。如果使用 next/link 呼叫此路由,則必須傳入 prefetch={false} 以防止在連結預取期間呼叫 clearPreviewData。
如果在 setPreviewData 呼叫中指定了路徑,則必須將相同的路徑傳遞給 clearPreviewData
export default function handler(req, res) {
const { path } = req.query
res.clearPreviewData({ path })
}previewData 大小限制
您可以將一個物件傳遞給 setPreviewData,並在 getStaticProps 中使其可用。但是,由於資料將儲存在 cookie 中,因此存在大小限制。目前,預覽資料限制為 2KB。
適用於 getServerSideProps
預覽模式也適用於 getServerSideProps。它也將在包含 preview 和 previewData 的 context 物件上可用。
須知:在使用預覽模式時,不應設定
Cache-Control標頭,因為它無法繞過。我們建議改用 ISR。
適用於 API 路由
API 路由將在請求物件下訪問 preview 和 previewData。例如
export default function myApiRoute(req, res) {
const isPreview = req.preview
const previewData = req.previewData
// ...
}每個 next build 都是唯一的
當 next build 完成時,繞過 cookie 值和加密 previewData 的私鑰都會更改。這確保了繞過 cookie 無法被猜測。
須知:要在本地透過 HTTP 測試預覽模式,您的瀏覽器需要允許第三方 cookie 和本地儲存訪問。
這有幫助嗎?