Next.js 快取重新驗證與清除
在開發具有資料互動的應用時,你必須精確控制資料何時「過期」。如果快取太久,使用者會看到舊資訊;如果完全不快取,伺服器負擔會太重。Next.js 提供了幾種靈活的機制來處理快取重新驗證 (Revalidation)。
時間基礎重新驗證 (Time-based Revalidation)
這是一種自動化的方式,告訴 Next.js 資料每隔一段時間後就應該被視為過期。
// 每一小時重新驗證一次 fetch 請求
fetch('https://api.example.com/data', { next: { revalidate: 3600 } });
這適用於資料變動頻率固定且對即時性要求不極端的場景(例如:天氣報報、新聞清單)。
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) 模式:
- 當使用者請求資料時,Next.js 先返回快取中的舊資料(快速)。
- 同時在背景觸發重新擷取。
- 一旦新資料擷取成功,Next.js 會更新快取。
- 下一次請求時,使用者就會看到最新資料了。
設定整站的預設值
你可以在 layout.js 或 page.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-store或revalidate: 0)。
這能幫助你快速診斷 revalidate 設定是否生效。
小結
- 靜態內容:不設定 revalidate(永久快取)。
- 定期更新:使用
next: { revalidate: N }。 - 資料變更驅動:在 Server Actions 中使用
revalidateTag。
正確使用快取驗證,能讓你的應用在「回應速度」與「資料真度」之間取得完美的平衡。