Next.js 快取重新驗證與清除

在開發具有資料互動的應用時,你必須精確控制資料何時「過期」。如果快取太久,使用者會看到舊資訊;如果完全不快取,伺服器負擔會太重。Next.js 提供了幾種靈活的機制來處理快取重新驗證 (Revalidation)。

時間基礎重新驗證 (Time-based Revalidation)

這是一種自動化的方式,告訴 Next.js 資料每隔一段時間後就應該被視為過期。

// 每一小時重新驗證一次 fetch 請求
fetch('https://api.example.com/data', { next: { revalidate: 3600 } });

這適用於資料變動頻率固定且對即時性要求不極端的場景(例如:天氣報報、新聞清單)。

行為模式 (Stale):當設定的時間 3600 秒到了之後,第一位進站的使用者仍然會看到舊資料。Next.js 會在背景默默觸發更新 (Revalidate),直到更新完成後,第二位使用者才會看到新資料。

隨需重新驗證 (On-demand Revalidation)

這是最精確的做法。當資料在資料庫中被更改(通常是在 Server Action 內)時,你主動告訴 Next.js:「這筆資料已經變了,請立即丟棄快取」。

與時間基礎不同,隨需驗證會立即清除快取。下一次請求(或是同一次 Server Action 重導向後的頁面渲染)絕對會拿到最新的即時資料。

路徑重新驗證:revalidatePath

這會清除特定路徑下所有請求的快取。

'use server';

import { revalidatePath } from 'next/cache';

export async function submitPost() {
  await db.posts.create({ ... });

  // 清除首頁的快取,確保新貼文出現
  revalidatePath('/');
}

標籤重新驗證:revalidateTag

這是一種更細緻的方法。你可以為特定的 fetch 請求打上標籤 (Tag),然後針對該標籤進行無效化。

// 1. 在擷取資料時加上標籤
const res = await fetch('...', { next: { tags: ['posts'] } });

// 2. 在 Action 中針對標籤更新
import { revalidateTag } from 'next/cache';

export async function addPost() {
  // ...
  revalidateTag('posts'); // 標記為 'posts' 的所有請求都會被重新驗證
}

快取重新驗證的原理

Next.js 採用的是 Stale-While-Revalidate (SWR) 模式:

  1. 當使用者請求資料時,Next.js 先返回快取中的舊資料(快速)。
  2. 同時在背景觸發重新擷取。
  3. 一旦新資料擷取成功,Next.js 會更新快取。
  4. 下一次請求時,使用者就會看到最新資料了。

設定整站的預設值

你可以在 layout.jspage.js 中導出 revalidate 變數,設定該分段下所有 fetch 的預設頻率:

export const revalidate = 60; // 每分鐘更新

結合 use cache 指令

隨著 Next.js 16 引入 use cache 指令,revalidateTag 的角色變得更加重要。它不僅能清除 fetch 的快取,也能清除被 cacheTag 標記的任何函式快取。

// data.ts
export async function getStock(sku: string) {
  'use cache';
  cacheTag('stock-' + sku);
  // ...
}

// actions.ts
export async function updateStock(sku: string) {
  // ... 更新資料庫
  revalidateTag('stock-' + sku); // 這會一併清除上面的 getStock 快取
}

如何確認快取狀態?

你可能常會問:「我怎麼知道現在抓到的是快取還是新資料?」

Next.js 提供了強大的 logging 功能。請在 next.config.js 中開啟它:

// next.config.mjs
const nextConfig = {
  logging: {
    fetches: {
      fullUrl: true, // 顯示完整的 URL
    },
  },
};

export default nextConfig;

開啟後,當你運行 npm run dev,你會在終端機看到詳細的快取狀態:

  • HIT: 命中快取(未過期)。
  • MISS: 未命中快取(或是第一次請求)。
  • SKIP: 跳過快取(設定了 no-storerevalidate: 0)。

這能幫助你快速診斷 revalidate 設定是否生效。

小結

  • 靜態內容:不設定 revalidate(永久快取)。
  • 定期更新:使用 next: { revalidate: N }
  • 資料變更驅動:在 Server Actions 中使用 revalidateTag

正確使用快取驗證,能讓你的應用在「回應速度」與「資料真度」之間取得完美的平衡。