預覽模式
注意:此功能已被草稿模式取代。
範例
- Agility CMS 範例 (Demo)
- Builder.io 範例 (Demo)
- ButterCMS 範例 (Demo)
- Contentful 範例 (Demo)
- Cosmic 範例 (Demo)
- DatoCMS 範例 (Demo)
- DotCMS 範例 (Demo)
- Drupal 範例 (Demo)
- Enterspeed 範例 (Demo)
- GraphCMS 範例 (Demo)
- Keystone 範例 (Demo)
- Kontent.ai 範例 (Demo)
- Makeswift 範例 (Demo)
- Plasmic 範例 (Demo)
- Prepr 範例 (Demo)
- Prismic 範例 (Demo)
- Sanity 範例 (Demo)
- Sitecore XM Cloud 範例 (Demo)
- Storyblok 範例 (Demo)
- Strapi 範例 (Demo)
- TakeShape 範例 (Demo)
- Tina 範例 (Demo)
- Umbraco 範例 (Demo)
- Umbraco Heartcore 範例 (Demo)
- Webiny 範例 (Demo)
- WordPress 範例 (Demo)
- Blog Starter 範例 (Demo)
在Pages 文件和資料抓取文件中,我們討論了如何在建置時預先渲染頁面(靜態產生),使用 getStaticProps
和 getStaticPaths
。
當您的頁面從 Headless CMS 抓取資料時,靜態產生非常有用。但是,當您在 Headless CMS 上撰寫草稿並想要預覽頁面上的草稿時,這並非理想的做法。您會希望 Next.js 在請求時而非建置時渲染這些頁面,並抓取草稿內容而非已發布的內容。您會希望 Next.js 僅針對此特定情況繞過靜態產生。
Next.js 有一個稱為預覽模式的功能,可以解決此問題。以下是如何使用它的說明。
步驟 1:建立並存取預覽 API 路由
如果您不熟悉 Next.js API 路由,請先查看API 路由文件。
首先,建立預覽 API 路由。它可以有任何名稱 - 例如 pages/api/preview.js
(如果使用 TypeScript,則為 .ts
)。
在此 API 路由中,您需要在 response 物件上呼叫 setPreviewData
。setPreviewData
的引數應該是一個物件,getStaticProps
可以使用它(稍後會詳細介紹)。目前,我們將使用 {}
。
export default function handler(req, res) {
// ...
res.setPreviewData({})
// ...
}
res.setPreviewData
會在瀏覽器上設定一些 cookies,以啟用預覽模式。任何發送到 Next.js 且包含這些 cookies 的請求都會被視為預覽模式,並且靜態生成頁面的行為將會改變 (稍後會詳細說明)。
您可以透過建立如下的 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
cookies 將會在這次請求中被設定。
從您的 Headless CMS 安全地存取
實際上,您會希望從您的 headless CMS 安全地呼叫此 API 路由。具體的步驟會因您使用的 headless CMS 而異,但以下是一些您可以採取的常見步驟。
這些步驟假設您使用的 headless CMS 支援設定自訂預覽網址。如果它不支援,您仍然可以使用此方法來保護您的預覽網址,但您需要手動建構和存取預覽網址。
首先,您應該使用您選擇的 token 產生器建立一個密碼 token 字串。這個密碼只有您的 Next.js 應用程式和您的 headless CMS 知道。這個密碼可以防止沒有權限存取您 CMS 的人存取預覽網址。
其次,如果您的 headless CMS 支援設定自訂預覽網址,請指定以下內容作為預覽網址。這假設您的預覽 API 路由位於 pages/api/preview.js
。
https://<your-site>/api/preview?secret=<token>&slug=<path>
<your-site>
應該是您的部署網域。<token>
應該替換為您產生的密碼 token。<path>
應該是您想要預覽頁面的路徑。如果您想要預覽/posts/foo
,那麼您應該使用&slug=/posts/foo
。
您的 headless CMS 可能允許您在預覽網址中包含變數,以便可以根據 CMS 的資料動態設定 <path>
,例如:&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)
}
如果成功,瀏覽器將會被重新導向到您想要預覽的路徑,並且預覽模式 cookies 也會被設定。
步驟 2:更新 getStaticProps
下一步是更新 getStaticProps
以支援預覽模式。
如果您請求一個具有 getStaticProps
的頁面,並且設定了預覽模式 cookies (透過 res.setPreviewData
),那麼 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
提取不同的資料。
例如,您的 headless CMS 可能針對草稿文章有不同的 API 端點。如果是這樣,您可以使用 context.preview
來修改 API 端點網址,如下所示
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' : ''}`)
// ...
}
就是這樣!如果您從您的 headless CMS 或手動存取預覽 API 路由 (帶有 secret
和 slug
),您現在應該能夠看到預覽內容。並且如果您在未發布的情況下更新草稿,您應該能夠預覽草稿。
將此設定為您的 headless CMS 上的預覽網址或手動存取,您應該就能夠看到預覽。
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
})
清除預覽模式 cookies
預設情況下,預覽模式 cookies 沒有設定到期日,因此預覽會話會在瀏覽器關閉時結束。
要手動清除預覽模式 cookies,請建立一個呼叫 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 測試預覽模式,您的瀏覽器需要允許第三方 cookies 和本地儲存存取權。
這有幫助嗎?