Rick's DevNotes
筆記關於我作品集
筆記類別
  • 全部
  • DockerDocker
  • NetworkNetwork
  • RxJSRxJS
  • NginxNginx
  • TypeScriptTypeScript
  • Data_Structure_And_AlgorithmData Structure And Algorithm
  • JavaScriptJavaScript
  • PostgreSQLPostgreSQL
  • ReactReact
  • GitGit

© 2026 Rick's DevNotes. All rights reserved.

# Cache# Performance# Next.js

更新時間:2025/08/19

建立時間:2024/11/28

Next.js 筆記 - Hydration & Serialization 以及四種快取機制探討

在實際使用 Next.js 的 app router 開發時,除了直覺的檔案結構外,其實「Serialization 與 Hydration 的原理」以及「四層快取機制」的運作方式也變得非常重要。

這篇筆記將系統性整理 Hydration/Serialization 流程與 Next.js 主要快取策略,並結合自身開發經驗,方便日後查閱與排解疑難。


Hydration 與 Dehydration 是什麼?

React Server Components(RSC)會先在伺服器生成 HTML;Hydration 則於瀏覽器端注入 JavaScript,使原本靜態的標記具備互動性。相對地,Dehydration 會將可序列化的狀態(通常為 JSON)封裝並傳送至另一端,於下次請求時再進行 hydrate 以恢復狀態。可以類比為旅途中先將行李壓縮打包(dehydrate),抵達目的地後再展開(hydrate),確保 SSR 與 CSR 無縫銜接。

Hydration 與 Dehydration 的對照

階段執行位置主要動作
Dehydration伺服器 → HTML將記憶體中的狀態(如 RSC 樹、React Query Cache)序列化為 JSON,可嵌入 HTML/RSC Payload。
Hydration瀏覽器 → JSReact 讀取序列化結果,還原物件 並 掛載事件,使靜態 HTML 變為可互動。

兩者是一組「寫出/讀回」過程:正確序列化是成功 Hydration 的前提。

為什麼需要序列化?

  • 跨邊界:僅當資料必須在 Server ↔ Client 間傳遞(如 Server Component 將 props 傳給 Client Component,或 Server Action 回傳到瀏覽器)時,React 會將值寫入 RSC Payload。
  • 目的:確保瀏覽器端能在 Hydration 階段還原(hydrate)出對應的 JavaScript 物件,並掛上事件監聽器。

NOTE: 簡單來說, 序列化 就是定義「Server 可以傳哪些值到 Client」。

允許序列化的型別

類別可序列化的型別
原始型別string · number · bigint · boolean · null · undefined · 全域 Symbol (Symbol.for)
可迭代集合Array · String · Map · Set · 所有 TypedArray · ArrayBuffer
特殊值Date · Promise · 純物件 (Plain Object)
React 相關Server / Client Component 的 JSX element · 標記 'use server' 的 Server Function

不可序列化的常見案例

  • 非 Server Function 的一般 函式
  • Class 及其實例
  • 非全域 Symbol(如 Symbol('foo'))
  • null prototype 物件

傳遞這些值會在 Build 或 Hydration 階段拋錯。


四種快取機制

Next.js 15 提供「層層遞進」的快取模型,從最短暫到最持久依序為:Request Memoization → Data Cache → Full Route Cache → Router Cache。以下用故事化敘事,把抽象名詞變成場景。

1. Request Memoization:同一次請求,只做一次工

情境:<Layout>、<Sidebar> 和 <Page> 同時呼叫 GET /api/users。

在同一個伺服器請求生命週期裡,Next.js 會先檢查 fetch 的 URL、HTTP 方法(僅限 GET)、init 參數是否完全相同。如果符合,就把第一次的 Promise 回傳值暫存於記憶體,再將之分享給其他需要的元件。請求結束,記憶體即被釋放,任何後續請求都要重新來過。

限制

  1. 僅 GET 方法具備自動去重功能;POST /users 仍會分別執行。
  2. Memoization 不跨請求、不跨使用者,更不會寫入磁碟。

NOTE: 這種快取機制讓開發者不必再糾結於「所有 fetch 一定要寫在最上層(如 layout 或 page)」的傳統做法。我們可以在任何需要資料的元件(甚至是巢狀元件)中直接呼叫 fetch,只要請求參數相同,Next.js 會自動共用同一份資料,避免重複請求。

2. Data Cache:後端的「公用冰箱」

情境:/products 靜態列表一小時才更新一次。

Data Cache 就是把 fetch 拿到的資料,直接存到寫入可持久化的伺服端快取上(像 Vercel 的 Global Data Cache 或我們自己主機預設的記憶體+檔案系統)。這樣一來,不管是哪個使用者、發出多少次請求,都會拿到同一份快取資料。這份資料會一直存在,直到我們設定的時間到了,或我們手動把它清掉。

  • 如何使用:在 fetch() 所在檔案(RSC 或 Server Action)設定 next: { cache: 'force-cache' }即可。
  • 有效期限:可透過 next: { revalidate: 60 } 指定秒數;到期後第一次請求仍使用舊資料,但背景會 revalidate。
  • 主動失效:
    • revalidatePath('/products'):以路徑為基準。
    • revalidateTag('product'):以標籤分類。

NOTE: 以 Next.js 15 而言,fetch 在未指定 cache 參數時,相當於將 cache 設為 no-store。

NOTE: Data Cache 被失效時,若該路由同時有 Full Route Cache,Next.js 會連同整頁重新渲染並覆寫舊快取。

3. Full Route Cache:整盤料理,煮好再端上桌

情境:部落格文章於建置階段就渲染為靜態 HTML。

Full Route Cache 把整個路由的最終 render 結果(HTML + RSC Payload)寫入硬碟。使用者請求相同 URL(含 query string)時,伺服器直接吐出現成結果,完全不跑 React Tree——真正的「0ms TTFB」。

  • 生成時機:
    1. npm run build 時由 SSG 產生。
    2. 或在 dynamic = 'force-static'、revalidate = 60 等模式下,第一次請求時由 ISR 背景產生。
  • 如何跳脫:
    1. 使用 Dynamic API(cookies, headers, searchParams)。
    2. 在 route.ts 內寫 POST、PUT 等非 GET handler。
    3. dynamic = 'force-dynamic' 或 revalidate = 0 明示關閉。
  • 持久性:部署新版本會整包清空,但 Data Cache 不受影響。

4. Router Cache:瀏覽器端的時間膠囊

情境:使用者由 /posts 點擊到 /posts/1,再按瀏覽器返回。

Router Cache 儲存在瀏覽器記憶體,主要目標是加速導航與 Prefetch。當我們滑到頁面中某連結時,Next.js 可能已先向伺服器要 RSC Payload 並存下;之後點擊不需再等待請求就能渲染,體感近似 SPA。

  • 失效條件:
    1. 執行 router.refresh()。
    2. 在 Server Action 呼叫 revalidatePath 或 revalidateTag。
    3. 在 Server Action 呼叫 cookies.set() 或 cookies.delete()(避免登入狀態改變卻仍顯舊畫面)。

NOTE: 只對 Client 端生效;直接打 URL 或硬刷新仍會重新造訪伺服器。


快取層之間的連動規則

被失效連動影響
Data Cache同路由的 Full Route Cache 也會被重新渲染
Full Route CacheData Cache 仍保留;新渲染會優先讀舊 Data Cache
Router Cache與伺服器快取(Data / Full Route)無直接關聯
Request Memoization生命週期最短,不影響任何其他層

實務 Tips

  1. 資料變動快? fetch 資料時,cache 機制以 no-store 為主。
  2. 需要 SEO? 靜態生產或 ISR,留給 Full Route Cache。
  3. 跨 page 共用 JSON? 用 Data Cache + revalidateTag。
  4. 單次請求多處重複 fetch? 交給 Request Memoization,避免多餘的 API Request。
  5. 登入、購物車一變就要即時? 操作 cookies 後 router.refresh(),確保 Router Cache 清乾淨。

參考資料

  • Guides: Caching | Next.js
  • Serializable types returned by Server Components
  • Advanced Server Rendering