介紹/指南/ISR

如何實作增量靜態再生 (ISR)

範例

增量靜態再生 (ISR) 讓您能夠:

  • 更新靜態內容而無需重建整個網站
  • 透過提供預渲染的靜態頁面來減少伺服器負載
  • 確保自動為頁面添加正確的 cache-control 標頭
  • 處理大量內容頁面而無需長時間的 next build

以下是一個最小範例:

import type { GetStaticPaths, GetStaticProps } from 'next'

interface Post {
  id: string
  title: string
  content: string
}

interface Props {
  post: Post
}

export const getStaticPaths: GetStaticPaths = async () => {
  const posts = await fetch('https://api.vercel.app/blog').then((res) =>
    res.json()
  )
  const paths = posts.map((post: Post) => ({
    params: { id: String(post.id) },
  }))

  // 我們只會在建置時預渲染這些路徑
  // { fallback: 'blocking' } 會在路徑不存在時按需伺服器渲染頁面
  return { paths, fallback: false }
}

export const getStaticProps: GetStaticProps<Props> = async ({
  params,
}: {
  params: { id: string }
}) => {
  const post = await fetch(`https://api.vercel.app/blog/${params.id}`).then(
    (res) => res.json()
  )

  return {
    props: { post },
    // Next.js 會在請求進來時使快取失效
    // 最多每 60 秒一次
    revalidate: 60,
  }
}

export default function Page({ post }: Props) {
  return (
    <main>
      <h1>{post.title}</h1>
      <p>{post.content}</p>
    </main>
  )
}

這個範例的運作方式如下:

  1. next build 期間,所有已知的部落格文章會被生成(此範例中有 25 篇)
  2. 對這些頁面的所有請求(例如 /blog/1)會被快取且立即回應
  3. 60 秒過後,下一個請求仍會顯示快取的(過期)頁面
  4. 快取失效後,新版本的頁面會在背景開始生成
  5. 成功生成後,Next.js 會顯示並快取更新後的頁面
  6. 如果請求 /blog/26,Next.js 會按需生成並快取此頁面

參考資料

函式

範例

使用 res.revalidate() 進行按需驗證

對於更精確的重新驗證方法,使用 res.revalidate 從 API 路由按需生成新頁面。

例如,可以呼叫 /api/revalidate?secret=<token> 這個 API 路由來重新驗證指定的部落格文章。建立一個只有您的 Next.js 應用程式知道的秘密令牌。這個秘密將用於防止未經授權的存取重新驗證 API 路由。

import type { NextApiRequest, NextApiResponse } from 'next'

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  // 檢查秘密以確認這是有效的請求
  if (req.query.secret !== process.env.MY_SECRET_TOKEN) {
    return res.status(401).json({ message: '無效的令牌' })
  }

  try {
    // 這應該是實際路徑而非重寫路徑
    // 例如對於 "/posts/[id]" 這應該是 "/posts/1"
    await res.revalidate('/posts/1')
    return res.json({ revalidated: true })
  } catch (err) {
    // 如果發生錯誤,Next.js 會繼續
    // 顯示最後成功生成的頁面
    return res.status(500).send('重新驗證錯誤')
  }
}

如果您使用按需重新驗證,則不需要在 getStaticProps 中指定 revalidate 時間。Next.js 會使用預設值 false(不重新驗證),並且只在呼叫 res.revalidate() 時按需重新驗證頁面。

處理未捕獲的例外

如果在處理背景重新生成時 getStaticProps 中發生錯誤,或者您手動拋出錯誤,則會繼續顯示最後成功生成的頁面。在下一個後續請求中,Next.js 會重試呼叫 getStaticProps

import type { GetStaticProps } from 'next'

interface Post {
  id: string
  title: string
  content: string
}

interface Props {
  post: Post
}

export const getStaticProps: GetStaticProps<Props> = async ({
  params,
}: {
  params: { id: string }
}) => {
  // 如果此請求拋出未捕獲的錯誤,Next.js 將
  // 不會使目前顯示的頁面失效,並且
  // 在下一個請求時重試 getStaticProps
  const res = await fetch(`https://api.vercel.app/blog/${params.id}`)
  const post: Post = await res.json()

  if (!res.ok) {
    // 如果有伺服器錯誤,您可能想要
    // 拋出錯誤而非回傳,以便快取不會更新
    // 直到下一次成功的請求
    throw new Error(`取得文章失敗,收到狀態碼 ${res.status}`)
  }

  return {
    props: { post },
    // Next.js 會在請求進來時使快取失效
    // 最多每 60 秒一次
    revalidate: 60,
  }
}

自訂快取位置

如果您想將快取的頁面和資料持久化到持久儲存,或者在 Next.js 應用程式的多個容器或實例之間共享快取,可以配置 Next.js 快取位置。了解更多

疑難排解

在本地開發中除錯快取資料

如果您使用 fetch API,可以添加額外的日誌記錄來了解哪些請求被快取或未快取。了解更多關於 logging 選項的資訊

next.config.js
module.exports = {
  logging: {
    fetches: {
      fullUrl: true,
    },
  },
}

驗證正確的生產環境行為

若要驗證您的頁面在生產環境中是否正確快取及重新驗證,可以在本地執行 next build 後再執行 next start 來運行 Next.js 的生產環境伺服器。

這將讓您測試 ISR (Incremental Static Regeneration) 行為在生產環境中的運作方式。如需進一步除錯,請在 .env 檔案中加入以下環境變數:

.env
NEXT_PRIVATE_DEBUG_CACHE=1

這會讓 Next.js 伺服器在主控台記錄 ISR 快取命中與未命中的情況。您可以檢查輸出內容,查看哪些頁面是在 next build 時生成的,以及當路徑被按需存取時頁面是如何更新的。

注意事項

  • ISR 僅在使用 Node.js 運行環境 (預設) 時支援。
  • 建立 靜態匯出 (Static Export) 時不支援 ISR。
  • 中介軟體 (Middleware) 不會在按需 ISR 請求時執行,這意味著任何路徑重寫或中介軟體中的邏輯都不會生效。請確保您重新驗證的是確切路徑。例如 /post/1 而非重寫後的 /post-1

平台支援

部署選項是否支援
Node.js 伺服器
Docker 容器
靜態匯出
轉接器 (Adapters)依平台而定

了解如何在使用 Next.js 自架時 設定 ISR

版本歷史

版本變更
v14.1.0自訂 cacheHandler 功能穩定。
v13.0.0引入 App Router。
v12.2.0Pages Router: 按需 ISR 功能穩定。
v12.0.0Pages Router: 新增 Bot-aware ISR 回退機制
v9.5.0Pages Router: 引入穩定的 ISR 功能

On this page