增量靜態再生 (ISR)

範例

Next.js 允許您在網站建置完成後,仍能建立或更新靜態頁面。增量靜態再生 (ISR) 讓您可以針對每個頁面使用靜態生成功能,無需重新建置整個網站。透過 ISR,您可以在擴展至數百萬個頁面的同時,保留靜態生成的優勢。

須知事項:目前 edge 運行時 與 ISR 不相容,但您可以透過手動設定 cache-control 標頭來利用 stale-while-revalidate 機制。

要使用 ISR,請在 getStaticProps 中加入 revalidate 屬性:

function Blog({ posts }) {
  return (
    <ul>
      {posts.map((post) => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  )
}

// 此函式會在伺服器端的建置階段被呼叫。
// 如果啟用了重新驗證且收到新請求,它可能會在無伺服器函式上再次被呼叫
export async function getStaticProps() {
  const res = await fetch('https://.../posts')
  const posts = await res.json()

  return {
    props: {
      posts,
    },
    // Next.js 將嘗試重新生成頁面:
    // - 當收到請求時
    // - 最多每 10 秒一次
    revalidate: 10, // 單位為秒
  }
}

// 此函式會在伺服器端的建置階段被呼叫。
// 如果路徑尚未生成,它可能會在無伺服器函式上再次被呼叫
export async function getStaticPaths() {
  const res = await fetch('https://.../posts')
  const posts = await res.json()

  // 根據文章內容取得我們想要預先渲染的路徑
  const paths = posts.map((post) => ({
    params: { id: post.id },
  }))

  // 我們將在建置階段僅預先渲染這些路徑。
  // { fallback: 'blocking' } 會在路徑不存在時按需進行伺服器端渲染
  return { paths, fallback: 'blocking' }
}

export default Blog

當請求一個在建置階段預先渲染的頁面時,最初會顯示快取的頁面。

  • 在初始請求之後、10 秒內對該頁面的任何請求也將使用快取並立即回應
  • 10 秒過後,下一個請求仍會顯示快取的(過期)頁面
  • Next.js 會在背景觸發頁面的重新生成
  • 一旦頁面成功生成,Next.js 將使快取失效並顯示更新後的頁面。如果背景重新生成失敗,舊頁面將保持不變

當請求一個尚未生成的路徑時,Next.js 會在首次請求時進行伺服器端渲染。後續請求將從快取中提供靜態檔案。在 Vercel 上,ISR 會全域持久化快取並處理回滾

須知事項:請檢查您的上游資料提供者是否預設啟用了快取。您可能需要停用它(例如設定 useCdn: false),否則重新驗證將無法獲取新資料來更新 ISR 快取。當端點返回 Cache-Control 標頭時,快取可能會發生在 CDN 層級。

按需重新驗證

如果您將 revalidate 時間設定為 60,所有訪客將在一分鐘內看到相同的生成版本。唯一使快取失效的方法是等一分鐘過後有人訪問該頁面。

v12.2.0 開始,Next.js 支援按需增量靜態再生,可以手動清除特定頁面的 Next.js 快取。這讓您在以下情況更容易更新網站:

  • 您的無頭 CMS 內容被建立或更新時
  • 電子商務元資料變更時(價格、描述、類別、評論等)

getStaticProps 中,您不需要指定 revalidate 來使用按需重新驗證。如果省略 revalidate,Next.js 將使用預設值 false(不重新驗證),僅在呼叫 revalidate() 時按需重新驗證頁面。

須知事項中介軟體 不會針對按需 ISR 請求執行。相反,您需要在想要重新驗證的 確切 路徑上呼叫 revalidate()。例如,如果您有 pages/blog/[slug].js 和一個從 /post-1 重寫到 /blog/post-1 的路由,您需要呼叫 res.revalidate('/blog/post-1')

使用按需重新驗證

首先,建立一個只有您的 Next.js 應用知道的秘密令牌。此秘密將用於防止未經授權存取重新驗證 API 路由。您可以透過以下 URL 結構存取該路由(手動或透過 webhook):

終端機
https://<your-site.com>/api/revalidate?secret=<token>

接著,將該秘密新增為應用程式的 環境變數。最後,建立重新驗證 API 路由:

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

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

查看我們的示範 以了解按需重新驗證的實際運作並提供反饋。

在開發期間測試按需 ISR

當使用 next dev 在本機運行時,getStaticProps 會在每個請求上被呼叫。要驗證您的按需 ISR 配置是否正確,您需要建立一個 生產版本 並啟動 生產伺服器

終端機
$ next build
$ next start

然後,您可以確認靜態頁面是否已成功重新驗證。

錯誤處理與重新驗證

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

export async function getStaticProps() {
  // 如果此請求拋出未捕獲的錯誤,Next.js 將
  // 不會使當前顯示的頁面失效,並在
  // 下一個請求時重試 getStaticProps
  const res = await fetch('https://.../posts')
  const posts = await res.json()

  if (!res.ok) {
    // 如果是伺服器錯誤,您可能希望
    // 拋出錯誤而非返回,這樣快取將不會更新
    // 直到下一次成功的請求
    throw new Error(`獲取文章失敗,收到狀態碼 ${res.status}`)
  }

  // 如果請求成功,返回文章
  // 並每 10 秒重新驗證一次
  return {
    props: {
      posts,
    },
    revalidate: 10,
  }
}

自託管 ISR

增量靜態再生 (ISR) 在 自託管 Next.js 網站 上開箱即用,當您使用 next start 時即可使用。

當部署到容器編排系統如 KubernetesHashiCorp Nomad 時,可以使用此方法。預設情況下,生成的資源將儲存在每個 pod 的記憶體中。這意味著每個 pod 都有自己的靜態檔案副本。在該特定 pod 收到請求之前,可能會顯示過期資料。

為確保所有 pod 之間的一致性,您可以停用記憶體快取。這將告知 Next.js 伺服器僅使用檔案系統中由 ISR 生成的資源。

您可以在 Kubernetes pod(或類似設定)中使用共享網路掛載,在不同容器之間重用相同的檔案系統快取。透過共享相同的掛載點,包含 next/image 快取的 .next 資料夾也將被共享和重用。

要停用記憶體快取,請在 next.config.js 檔案中將 isrMemoryCacheSize 設為 0

next.config.js
module.exports = {
  experimental: {
    // 預設為 50MB
    isrMemoryCacheSize: 0, // 快取大小(位元組)
  },
}

須知事項:根據您的共享掛載配置,您可能需要考慮多個 pod 同時嘗試更新快取時的競爭條件問題。

版本歷史

版本變更
v12.2.0按需 ISR 穩定版
v12.1.0新增按需 ISR(測試版)。
v12.0.0新增 Bot-aware ISR fallback
v9.5.0新增 Base Path 支援。

On this page