ข้ามไปยังเนื้อหา

สี่ Caching Layers

caching ของ Next.js คือ สี่ชั้นแยกกัน ได้แก่ Request Memoization, Data Cache, Full Route Cache และ Router Cache และตั้งแต่ v15 Data Cache เป็นแบบ opt-in เพราะ fetch ไม่ถูก cache โดย default อีกต่อไป

แต่ละชั้น cache คนละอย่าง อยู่คนละที่ และ invalidate คนละแบบ การแยกทั้งสี่ชั้นออกจากกันในหัวคือทักษะทั้งหมดของเรื่องนี้

LayerWhat it cachesWhere it livesLifetimeHow it is invalidated
Request Memoizationค่าที่ return จาก fetch / cache() ที่เหมือนกันใน render เดียวServer, per request (React)หนึ่ง request / render passจบเองเมื่อ render เสร็จ
Data Cacheผลของ data fetch ใช้ซ้ำข้าม request และ deployServer (persistent)จนกว่าจะ revalidaterevalidateTag, revalidatePath หรือ revalidate แบบ time-based
Full Route CacheRSC payload กับ HTML ของ route ที่ render แบบ staticServer (persistent, build หรือ runtime)จนกว่าจะ deploy หรือ data revalidaterevalidate data ข้างใต้ หรือ redeploy
Router CacheRSC payload ของ route ที่เข้าชมหรือ prefetchClient (browser memory)ตลอด session; staleTimes คุมความสดnavigation, router.refresh() หรือ Server Action revalidation

สามในสี่ชั้นอยู่บน server และซ้อนกัน Request Memoization dedupe call ที่เหมือนกันภายใน render เดียว Data Cache เก็บผล fetch ข้าม request Full Route Cache เก็บผล render ของ static route เพื่อให้ request ข้ามการ render ไปเลย

จุดที่มือใหม่พลาด ตั้งแต่ Next.js 15 fetch ธรรมดาไม่เขียนอะไรลง Data Cache เลย เรา opt-in ทีละ call

// Not cached — hits the origin on every request (the Next.js 15+ default)
await fetch('https://api.example.com/prices')
// Opt in to the Data Cache indefinitely
await fetch('https://api.example.com/config', { cache: 'force-cache' })
// Opt in with a 60-second lifetime
await fetch('https://api.example.com/feed', { next: { revalidate: 60 } })
// Opt in and tag for on-demand invalidation
await fetch('https://api.example.com/cart', { next: { tags: ['cart'] } })

ชั้นที่สี่อยู่ใน browser เมื่อเรา navigate หรือ hover <Link> ที่ prefetch ไว้ Next.js เก็บ RSC payload ของ route ไว้ใน memory ทำให้ back และ forward ทันที ตั้งแต่ v15 ค่า default ของ staleTimes.dynamic คือ 0 ดังนั้น dynamic page จะไม่ถูกเก็บฝั่ง client จนกว่าเราจะเพิ่มค่า

// next.config.ts — opt into holding dynamic routes for 30s in the Router Cache
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
experimental: {
staleTimes: { dynamic: 30 },
},
}
export default nextConfig

ใน Next.js 14 fetch default เป็น force-cache tutorial เลยสอนว่า “Next.js cache ทุกอย่างให้อัตโนมัติ” ซึ่ง ผิดสำหรับ v15 และ v16 วันนี้ caching เป็นแบบ layered และ opt-in fetch ที่ไม่ cache ทำให้ route กลายเป็น dynamic และ Data Cache ว่างจนกว่าเราจะร้องขอ GET Route Handler ก็เปลี่ยนแบบเดียวกัน เป็น dynamic จนกว่าเราจะเพิ่ม export const dynamic = 'force-static'

flowchart TD
  A["Browser navigates"] --> B["Router Cache (client)"]
  B -->|miss| C["Full Route Cache (server)"]
  C -->|miss or dynamic| D["Render Server Components"]
  D --> E["Request Memoization (per render)"]
  E --> F["Data Cache (server)"]
  F -->|miss| G["Origin: API or database"]
How a request falls through the four caches
ตั้งแต่ version ไหนที่ fetch ไม่ cache โดย default ใน Next.js?
ชั้นไหนในสี่ชั้นที่อยู่ใน browser?
ชั้นไหน dedupe fetch ที่เหมือนกันภายใน render เดียว?
revalidateTag invalidate อะไร?