สี่ Caching Layers
ไอเดียในหนึ่งประโยค
หัวข้อที่มีชื่อว่า “ไอเดียในหนึ่งประโยค”caching ของ Next.js คือ สี่ชั้นแยกกัน ได้แก่ Request Memoization, Data Cache, Full Route Cache และ Router Cache และตั้งแต่ v15 Data Cache เป็นแบบ opt-in เพราะ fetch ไม่ถูก cache โดย default อีกต่อไป
สี่ชั้น สี่หน้าที่
หัวข้อที่มีชื่อว่า “สี่ชั้น สี่หน้าที่”แต่ละชั้น cache คนละอย่าง อยู่คนละที่ และ invalidate คนละแบบ การแยกทั้งสี่ชั้นออกจากกันในหัวคือทักษะทั้งหมดของเรื่องนี้
| Layer | What it caches | Where it lives | Lifetime | How it is invalidated |
|---|---|---|---|---|
| Request Memoization | ค่าที่ return จาก fetch / cache() ที่เหมือนกันใน render เดียว | Server, per request (React) | หนึ่ง request / render pass | จบเองเมื่อ render เสร็จ |
| Data Cache | ผลของ data fetch ใช้ซ้ำข้าม request และ deploy | Server (persistent) | จนกว่าจะ revalidate | revalidateTag, revalidatePath หรือ revalidate แบบ time-based |
| Full Route Cache | RSC payload กับ HTML ของ route ที่ render แบบ static | Server (persistent, build หรือ runtime) | จนกว่าจะ deploy หรือ data revalidate | revalidate data ข้างใต้ หรือ redeploy |
| Router Cache | RSC payload ของ route ที่เข้าชมหรือ prefetch | Client (browser memory) | ตลอด session; staleTimes คุมความสด | navigation, router.refresh() หรือ Server Action revalidation |
ฝั่ง server: memoization, data และ route cache
หัวข้อที่มีชื่อว่า “ฝั่ง server: memoization, data และ route cache”สามในสี่ชั้นอยู่บน 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 indefinitelyawait fetch('https://api.example.com/config', { cache: 'force-cache' })
// Opt in with a 60-second lifetimeawait fetch('https://api.example.com/feed', { next: { revalidate: 60 } })
// Opt in and tag for on-demand invalidationawait fetch('https://api.example.com/cart', { next: { tags: ['cart'] } })ฝั่ง client: Router Cache
หัวข้อที่มีชื่อว่า “ฝั่ง client: Router Cache”ชั้นที่สี่อยู่ใน 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 Cacheimport type { NextConfig } from 'next'
const nextConfig: NextConfig = { experimental: { staleTimes: { dynamic: 30 }, },}
export default nextConfigทำไม “fetch ไม่ cache โดย default” ถึงสำคัญ
หัวข้อที่มีชื่อว่า “ทำไม “fetch ไม่ cache โดย default” ถึงสำคัญ”ใน 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"]