跳到內容

如何實現增量靜態再生 (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
 
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
  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 期間,所有已知部落格文章都會生成
  2. 對這些頁面(例如 /blog/1)發出的所有請求都會被快取並立即響應
  3. 60 秒過後,下一個請求仍將返回快取的(現在已過期)頁面
  4. 快取失效,並在後臺開始生成頁面的新版本
  5. 成功生成後,下一個請求將返回更新後的頁面並將其快取,以供後續請求使用
  6. 如果請求 /blog/26 並且它存在,則頁面將按需生成。此行為可以透過使用不同的 dynamicParams 值來更改。但是,如果帖子不存在,則返回 404。

參考

路由段配置

函式

示例

基於時間重驗證

這會在 /blog 上獲取並顯示部落格文章列表。一個小時過後,下一個訪問者仍會立即收到頁面快取(過期)版本以獲得快速響應。同時,Next.js 會在後臺觸發新版本的再生。一旦新版本成功生成,它會替換快取版本,後續訪問者將收到更新後的內容。

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 還是連線到資料庫,這都將使整個路由的快取失效。對該路由的下一個請求將觸發重新生成並提供最新資料,然後該資料將快取以供後續請求使用。

注意: revalidatePath 使快取條目失效,但重新生成發生在下一個請求上。如果您希望立即主動重新生成快取條目,而不是等待下一個請求,您可以使用 Pages 路由 res.revalidate 方法。我們正在努力新增新方法,以提供 App Router 的主動重新生成功能。

app/actions.ts
'use server'
 
import { revalidatePath } from 'next/cache'
 
export async function createPost() {
  // Invalidate the cache for the /posts route
  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'
  revalidateTag('posts')
}

處理未捕獲的異常

如果在嘗試重新驗證資料時發生錯誤,則上次成功生成的資料將繼續從快取中提供。在下一個請求中,Next.js 將重試重新驗證資料。瞭解更多關於錯誤處理的資訊

自定義快取位置

如果您希望將快取頁面和資料持久化到持久儲存,或在 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 期間生成了哪些頁面,以及在按需訪問路徑時頁面如何更新。

注意事項

  • ISR 僅在使用 Node.js 執行時(預設)時支援。
  • 建立靜態匯出時不支援 ISR。
  • 如果靜態渲染路由中有多個 fetch 請求,並且每個請求都有不同的 revalidate 頻率,則將使用最低時間作為 ISR。但是,這些重新驗證頻率仍將由資料快取遵循。
  • 如果路由上使用的任何 fetch 請求的 revalidate 時間為 0 或顯式 no-store,則該路由將動態渲染
  • 代理不會為按需 ISR 請求執行,這意味著代理中的任何路徑重寫或邏輯都不會應用。請確保您正在重新驗證精確的路徑。例如,/post/1 而不是重寫的 /post-1

平臺支援

部署選項支援
Node.js 伺服器
Docker 容器
靜態匯出
介面卡平臺特定

瞭解如何在自託管 Next.js 時配置 ISR

版本歷史

版本更改
v14.1.0自定義 cacheHandler 已穩定。
v13.0.0App Router 已引入。
v12.2.0Pages Router:按需 ISR 已穩定
v12.0.0Pages Router:添加了機器人感知 ISR 回退
v9.5.0Pages Router:引入了穩定的 ISR