跳到內容

增量靜態再生 (ISR)

範例

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

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

以下是一個最小範例

app/blog/[id]/page.tsx
interface Post {
  id: string
  title: string
  content: string
}
 
// Next.js will invalidate the cache when a
// request comes in, at most once every 60 seconds.
export const revalidate = 60
 
// We'll prerender only the params from `generateStaticParams` at build time.
// If a request comes in for a path that hasn't been generated,
// Next.js will server-render the page on-demand.
export const dynamicParams = true // or false, to 404 on unknown paths
 
export async function generateStaticParams() {
  const posts: Post[] = await fetch('https://api.vercel.app/blog').then((res) =>
    res.json()
  )
  return posts.map((post) => ({
    id: String(post.id),
  }))
}
 
export default async function Page({
  params,
}: {
  params: Promise<{ id: string }>
}) {
  const id = (await params).id
  const post: Post = await fetch(`https://api.vercel.app/blog/${id}`).then(
    (res) => res.json()
  )
  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 將隨需產生並快取此頁面

參考

路由區段設定

函式

範例

基於時間的重新驗證

這會在 /blog 上提取並顯示部落格文章列表。一小時後,此頁面的快取將在下次訪問頁面時失效。然後,在背景中,會產生具有最新部落格文章的頁面新版本。

app/blog/page.tsx
interface Post {
  id: string
  title: string
  content: string
}
 
export const revalidate = 3600 // invalidate every hour
 
export default async function Page() {
  const data = await fetch('https://api.vercel.app/blog')
  const posts: Post[] = await data.json()
  return (
    <main>
      <h1>Blog Posts</h1>
      <ul>
        {posts.map((post) => (
          <li key={post.id}>{post.title}</li>
        ))}
      </ul>
    </main>
  )
}

我們建議設定較長的重新驗證時間。例如,1 小時而不是 1 秒。如果您需要更高的精確度,請考慮使用隨需重新驗證。如果您需要即時資料,請考慮切換到動態渲染

使用 revalidatePath 隨需重新驗證

對於更精確的重新驗證方法,請使用 revalidatePath 函式隨需使頁面失效。

例如,在新增文章後會呼叫此伺服器行為。無論您在伺服器元件中如何檢索資料,無論是使用 fetch 還是連線到資料庫,這都會清除整個路由的快取,並允許伺服器元件提取最新的資料。

app/actions.ts
'use server'
 
import { revalidatePath } from 'next/cache'
 
export async function createPost() {
  // Invalidate the /posts route in the cache
  revalidatePath('/posts')
}

查看示範探索原始碼

使用 revalidateTag 隨需重新驗證

對於大多數用例,建議優先重新驗證整個路徑。如果您需要更精細的控制,可以使用 revalidateTag 函式。例如,您可以標記個別的 fetch 呼叫

app/blog/page.tsx
export default async function Page() {
  const data = await fetch('https://api.vercel.app/blog', {
    next: { tags: ['posts'] },
  })
  const posts = await data.json()
  // ...
}

如果您正在使用 ORM 或連線到資料庫,則可以使用 unstable_cache

app/blog/page.tsx
import { unstable_cache } from 'next/cache'
import { db, posts } from '@/lib/db'
 
const getCachedPosts = unstable_cache(
  async () => {
    return await db.select().from(posts)
  },
  ['posts'],
  { revalidate: 3600, tags: ['posts'] }
)
 
export default async function Page() {
  const posts = getCachedPosts()
  // ...
}

然後您可以在伺服器行為路由處理器中使用 revalidateTag

app/actions.ts
'use server'
 
import { revalidateTag } from 'next/cache'
 
export async function createPost() {
  // Invalidate all data tagged with 'posts' in the cache
  revalidateTag('posts')
}

處理未捕獲的例外

如果在嘗試重新驗證資料時擲回錯誤,則最後一次成功產生的資料將繼續從快取中提供。在下一個後續請求中,Next.js 將重試重新驗證資料。深入瞭解錯誤處理

自訂快取位置

快取和重新驗證頁面(使用增量靜態再生)使用相同的共用快取。當部署到 Vercel時,ISR 快取會自動持久化到持久儲存體。

當自行託管時,ISR 快取會儲存到 Next.js 伺服器上的檔案系統(磁碟上)。當使用 Pages Router 和 App Router 自行託管時,這會自動運作。

如果您想要將快取的頁面和資料持久化到持久儲存體,或在 Next.js 應用程式的多個容器或實例之間共用快取,則可以設定 Next.js 快取位置。深入瞭解

疑難排解

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

如果您正在使用 fetch API,則可以新增額外的記錄,以瞭解哪些請求已快取或未快取。深入瞭解 logging 選項

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

驗證正確的生產環境行為

若要驗證您的頁面在生產環境中是否已正確快取和重新驗證,您可以在本地透過執行 next build,然後執行 next start 來執行生產環境 Next.js 伺服器進行測試。

這將允許您測試 ISR 行為,使其如同在生產環境中運作一樣。為了進一步偵錯,請將以下環境變數新增至您的 .env 檔案

.env
NEXT_PRIVATE_DEBUG_CACHE=1

這會使 Next.js 伺服器控制台記錄 ISR 快取命中和未命中。您可以檢查輸出,以查看哪些頁面在 next build 期間產生,以及當隨需存取路徑時頁面如何更新。

注意事項

  • 僅在使用 Node.js 執行階段(預設)時才支援 ISR。
  • 建立靜態匯出時,不支援 ISR。
  • 如果您在靜態渲染的路由中有多個 fetch 請求,並且每個請求都有不同的 revalidate 頻率,則 ISR 將使用最短的時間。但是,這些重新驗證頻率仍將受到資料快取的尊重。
  • 如果路由上使用的任何 fetch 請求的 revalidate 時間為 0,或明確的 no-store,則路由將會動態渲染
  • 中介層不會針對隨需 ISR 請求執行,這表示中介層中的任何路徑重寫或邏輯都不會套用。請確保您正在重新驗證確切的路徑。例如,/post/1 而不是重寫的 /post-1

版本歷史

版本變更
v14.1.0自訂 cacheHandler 功能已穩定。
v13.0.0引入 App Router。
v12.2.0Pages Router:隨需 ISR 功能已穩定
v12.0.0Pages Router:新增Bot 感知 ISR 回退
v9.5.0Pages Router:引入穩定的 ISR