Cache Components trong Next.js 16 năm 2026: Hướng dẫn chi tiết use cache, PPR và câu hỏi phỏng vấn
Phân tích chuyên sâu về Cache Components trong Next.js 16: directive use cache ở ba phạm vi, Partial Pre-Rendering, cacheLife profiles, cacheTag, bảo mật cache và câu hỏi phỏng vấn thường gặp cho developer năm 2026.

Next.js 16 Cache Components đánh dấu bước chuyển đổi quan trọng nhất trong cách Next.js xử lý caching kể từ khi App Router được giới thiệu. Mô hình cũ mặc định cache mọi thứ và yêu cầu opt-out khi cần. Mô hình mới trong Next.js 16 không cache bất cứ thứ gì theo mặc định và yêu cầu opt-in thông qua directive "use cache".
Sự thay đổi này mang lại quyền kiểm soát chi tiết hơn cho developers, đồng thời giải quyết nhiều vấn đề về bảo mật và độ tươi mới của dữ liệu mà mô hình cũ gặp phải. Bài viết này phân tích toàn diện về Cache Components, Partial Pre-Rendering và các câu hỏi phỏng vấn thường gặp liên quan đến chủ đề này.
Next.js 16 chuyển từ caching ngầm định (mọi thứ được cache, opt-out bằng dynamic APIs) sang caching tường minh (không cache gì cả, opt-in bằng "use cache"). Thay đổi này ảnh hưởng đến routing, data fetching, rendering và cách các câu hỏi phỏng vấn được đặt ra.
Tại sao Next.js 16 thay thế Implicit Caching
Trong Next.js 15 và các phiên bản trước đó, fetch requests được cache mặc định ở cấp độ request. Route handlers và Server Components cũng được cache theo cách tương tự. Điều này dẫn đến một số vấn đề nghiêm trọng trong thực tế:
Dữ liệu cũ không mong muốn: Developers thường quên opt-out khỏi caching, dẫn đến việc người dùng nhìn thấy dữ liệu đã lỗi thời. Đặc biệt với các ứng dụng có dữ liệu thay đổi thường xuyên như dashboard hoặc trang quản trị.
Rủi ro bảo mật tiềm ẩn: Cache được chia sẻ giữa các users có thể vô tình expose dữ liệu nhạy cảm nếu không được cấu hình cẩn thận.
Khó debug: Khi dữ liệu không cập nhật như mong đợi, việc xác định nguyên nhân từ caching behavior ngầm định trở nên phức tạp.
Next.js 16 giải quyết những vấn đề này bằng cách đảo ngược mô hình: không có gì được cache trừ khi developer chủ động yêu cầu. Đây là triết lý "explicit over implicit" được áp dụng triệt để.
Directive use cache hoạt động ở ba phạm vi
Directive "use cache" có thể được áp dụng ở ba cấp độ khác nhau, mỗi cấp độ phục vụ các use cases riêng biệt.
Cấp độ File
Khi đặt "use cache" ở đầu file, toàn bộ module được cache như một đơn vị. Đây là lựa chọn phù hợp cho các trang tĩnh hoặc các trang hiếm khi thay đổi.
"use cache"
import { getPricingPlans } from "@/lib/data"
// Entire page is cached as a static shell
export default async function PricingPage() {
const plans = await getPricingPlans()
return (
<section>
{plans.map((plan) => (
<PricingCard key={plan.id} plan={plan} />
))}
</section>
)
}Cấp độ Component
Component-level caching cho phép cache từng component riêng lẻ. Props của component tự động trở thành một phần của cache key, đảm bảo mỗi tổ hợp props có cache entry riêng.
async function ProductRecommendations({ categoryId }: { categoryId: string }) {
"use cache"
// categoryId becomes part of the automatic cache key
const products = await getTopProducts(categoryId)
return (
<ul>
{products.map((p) => (
<li key={p.id}>{p.name} - {p.price}</li>
))}
</ul>
)
}Cấp độ Function
Function-level caching linh hoạt nhất, cho phép cache kết quả của các hàm fetch dữ liệu. Tham số của hàm tự động được bao gồm trong cache key.
import { cacheLife } from "next/cache"
export async function getArticleBySlug(slug: string) {
"use cache"
cacheLife("hours")
// slug is automatically included in the cache key
const article = await db.article.findUnique({ where: { slug } })
return article
}Ưu tiên function-level caching cho các data fetching functions để tái sử dụng cache across multiple components và pages.
Partial Pre-Rendering: Static Shell với Dynamic Holes
Partial Pre-Rendering (PPR) là tính năng đột phá trong Next.js 16, cho phép kết hợp static và dynamic content trong cùng một page một cách thông minh. Thay vì phải chọn giữa fully static hoặc fully dynamic, PPR cho phép pre-render một "static shell" và stream dynamic content vào các "holes" được đánh dấu bởi Suspense boundaries.
import { Suspense } from "react"
import { UserGreeting } from "@/components/UserGreeting"
import { StaticSidebar } from "@/components/StaticSidebar"
import { RecentActivity } from "@/components/RecentActivity"
export default function DashboardPage() {
return (
<div className="grid grid-cols-12 gap-6">
{/* Cached static shell - served instantly */}
<StaticSidebar />
<main className="col-span-9">
{/* Dynamic - streams in after static shell */}
<Suspense fallback={<GreetingSkeleton />}>
<UserGreeting />
</Suspense>
{/* Dynamic - streams independently */}
<Suspense fallback={<ActivitySkeleton />}>
<RecentActivity />
</Suspense>
</main>
</div>
)
}Trong ví dụ trên, StaticSidebar được serve ngay lập tức từ cache, trong khi UserGreeting và RecentActivity được stream độc lập khi dữ liệu sẵn sàng. Người dùng thấy layout chính ngay lập tức, cải thiện đáng kể perceived performance.
PPR hoạt động bằng cách:
- Pre-render static shell tại build time
- Serve static shell ngay lập tức khi có request
- Stream dynamic content vào các Suspense boundaries song song
- Mỗi dynamic section hoàn thành độc lập, không block lẫn nhau
Thêm cacheComponents: true vào next.config.ts. Flag duy nhất này kích hoạt PPR, directive "use cache" và toàn bộ hệ thống Cache Components. Không cần cấu hình thêm.
cacheLife Profiles: Thay thế revalidate
Next.js 16 giới thiệu cacheLife để thay thế option revalidate trong fetch. Thay vì chỉ định số giây cứng nhắc, developers sử dụng semantic profiles mô tả ý định caching.
import { cacheLife } from "next/cache"
export async function getExchangeRates() {
"use cache"
cacheLife("minutes") // Revalidates every few minutes
const rates = await fetch("https://api.exchangerate.host/latest")
return rates.json()
}
export async function getCompanyInfo() {
"use cache"
cacheLife("weeks") // Rarely changes
return db.company.findFirst()
}Các built-in profiles bao gồm: "seconds", "minutes", "hours", "days", "weeks", và "max" (cache vĩnh viễn cho đến khi invalidate thủ công).
Custom cacheLife Profiles
Đối với các yêu cầu cụ thể, developers có thể định nghĩa custom profiles trong next.config.ts:
import type { NextConfig } from "next"
const config: NextConfig = {
cacheComponents: true,
cacheLife: {
// Custom profile for product data
product: {
stale: 300, // Serve stale for 5 minutes
revalidate: 3600, // Revalidate in background every hour
expire: 86400, // Hard expire after 24 hours
},
},
}
export default configConditional cacheLife
Một tính năng mạnh mẽ là khả năng áp dụng cacheLife có điều kiện dựa trên kết quả của data fetching:
export async function getProduct(id: string) {
"use cache"
const product = await db.product.findUnique({ where: { id } })
if (!product) {
// Short cache for missing items (may appear soon)
cacheLife("minutes")
return null
}
// Longer cache for existing products
cacheLife("product")
return product
}Sẵn sàng chinh phục phỏng vấn React / Next.js?
Luyện tập với mô phỏng tương tác, flashcards và bài kiểm tra kỹ thuật.
cacheTag cho Targeted Invalidation
Trong khi cacheLife kiểm soát thời gian cache tồn tại, cacheTag cho phép invalidate cache một cách có chủ đích khi dữ liệu thay đổi. Đây là công cụ quan trọng cho các ứng dụng có dữ liệu được cập nhật thông qua user actions.
import { cacheLife, cacheTag } from "next/cache"
export async function getProductById(id: string) {
"use cache"
cacheTag(`product-${id}`, "products")
cacheLife("days")
return db.product.findUnique({ where: { id } })
}Khi sản phẩm được cập nhật, sử dụng revalidateTag trong Server Action để invalidate cache liên quan:
"use server"
import { revalidateTag } from "next/cache"
export async function updateProduct(id: string, data: ProductUpdate) {
await db.product.update({ where: { id }, data })
// Invalidate this specific product AND the product list
revalidateTag(`product-${id}`)
revalidateTag("products")
}Một cached function không có cacheTag() chỉ có thể hết hạn theo thời gian. Invalidation theo yêu cầu là không thể. Đây là lỗi thường gặp trong quá trình phát triển ban đầu và khó phát hiện cho đến khi dữ liệu cũ xuất hiện trên production.
Bảo mật: use cache và use cache private
Một trong những cải tiến bảo mật quan trọng nhất trong Next.js 16 là sự phân biệt rõ ràng giữa shared cache và private cache. Việc sử dụng sai directive có thể dẫn đến data leaks nghiêm trọng.
// WRONG: User data in shared cache - data leak risk
export async function getUserDashboard(userId: string) {
"use cache"
return db.user.findUnique({
where: { id: userId },
include: { orders: true, preferences: true },
})
}
// CORRECT: Private cache scoped to the current user
export async function getUserDashboard() {
"use cache: private"
cacheLife("minutes")
const session = await cookies()
const userId = session.get("userId")?.value
return db.user.findUnique({
where: { id: userId },
include: { orders: true, preferences: true },
})
}Sự khác biệt quan trọng:
| Directive | Phạm vi | Sử dụng khi |
|-----------|---------|-------------|
| "use cache" | Chia sẻ, tất cả người dùng | Dữ liệu công khai: bảng giá, bài viết, danh mục sản phẩm |
| "use cache: private" | Phiên người dùng riêng | Dữ liệu cá nhân: dashboard, cài đặt, lịch sử đơn hàng |
| "use cache: remote" | Chia sẻ, lưu trữ bên ngoài | Dữ liệu truy cập cao trong môi trường serverless |
Bất cứ khi nào cache dữ liệu phụ thuộc vào user session hoặc chứa thông tin cá nhân, BẮT BUỘC sử dụng "use cache: private". Việc quên điều này có thể expose dữ liệu của user A cho user B.
Migration từ Next.js 15 sang Cache Components
Quá trình migration đòi hỏi sự cẩn thận vì behavior mặc định đã thay đổi hoàn toàn. Có ba pitfalls chính cần lưu ý.
Pitfall 1: Closures trên request-scoped data. Nếu cached function tham chiếu đến giá trị từ scope bao quanh mà thay đổi theo request (header, cookie, user ID), việc chuyển đổi không đơn giản. Cần truyền giá trị đó làm argument tường minh hoặc sử dụng "use cache: private".
Pitfall 2: Conditional caching. Code trước đó wrap unstable_cache chỉ khi một điều kiện thỏa mãn cần được tái cấu trúc. "use cache" luôn hoạt động khi được áp dụng. Di chuyển điều kiện ra ngoài cached function.
Pitfall 3: Manual cache keys. unstable_cache yêu cầu mảng keyParts tường minh. Compiler "use cache" tự động tạo keys từ arguments cộng với build ID và function hash. Loại bỏ quản lý key thủ công là mục tiêu, nhưng cần xác minh rằng tất cả giá trị phân biệt cache đều là tham số function thực tế.
Câu hỏi phỏng vấn Next.js 16 Cache Components
Dưới đây là các câu hỏi phỏng vấn thường gặp về cache components next.js và partial pre-rendering, cùng với câu trả lời chi tiết.
Câu hỏi 1: Sự khác biệt giữa caching model trong Next.js 15 và Next.js 16 là gì?
Next.js 15 sử dụng implicit caching - mọi thứ được cache mặc định và developers phải opt-out. Next.js 16 sử dụng explicit caching - không có gì được cache mặc định và developers phải opt-in bằng "use cache" directive. Sự thay đổi này giúp tránh dữ liệu cũ không mong muốn và giảm rủi ro bảo mật từ shared cache.
Câu hỏi 2: Giải thích Partial Pre-Rendering hoạt động như thế nào?
PPR cho phép pre-render một static shell tại build time và stream dynamic content vào các "holes" được đánh dấu bởi Suspense boundaries tại request time. Static shell được serve ngay lập tức, cải thiện Time to First Byte, trong khi dynamic content streams độc lập. Điều này kết hợp ưu điểm của static generation với tính linh hoạt của server-side rendering.
Câu hỏi 3: Khi nào nên sử dụng "use cache: private" thay vì "use cache"?
Sử dụng "use cache: private" khi cache dữ liệu phụ thuộc vào user session hoặc chứa thông tin cá nhân như dashboard data, user preferences, order history. Cache này được scope riêng cho từng user. Sử dụng "use cache" cho dữ liệu công khai được chia sẻ giữa tất cả users như pricing plans, blog posts, product catalogs.
Câu hỏi 4: cacheLife profiles khác với revalidate option như thế nào?
revalidate chỉ định số giây cố định để cache tồn tại. cacheLife sử dụng semantic profiles như "minutes", "hours", "days" mô tả ý định caching, dễ đọc và maintain hơn. cacheLife cũng hỗ trợ custom profiles với ba tham số: stale (serve stale content), revalidate (background revalidation), và expire (hard expiration).
Câu hỏi 5: Làm thế nào để invalidate cache một cách có chủ đích trong Next.js 16?
Sử dụng cacheTag để gắn tags cho cached data và revalidateTag trong Server Actions để invalidate. Ví dụ, gắn tag product-${id} và products cho product data, sau đó gọi revalidateTag("product-123") khi product được update. Điều này cho phép invalidation chính xác mà không ảnh hưởng đến cache không liên quan.
Câu hỏi 6: Tại sao caching model mới được coi là an toàn hơn?
Mô hình mới an toàn hơn vì: (1) Không có gì được cache mặc định, tránh vô tình cache dữ liệu nhạy cảm; (2) Phân biệt rõ ràng giữa shared cache và private cache thông qua directives; (3) Developers phải chủ động quyết định cache gì, buộc suy nghĩ về data sensitivity; (4) Private cache được scope theo user session, ngăn chặn data leaks giữa users.
Để tìm hiểu thêm về các chủ đề liên quan, tham khảo câu hỏi phỏng vấn Next.js data fetching và module Next.js Server Actions.
Checklist thực hành cho Cache Components
- Kích hoạt
cacheComponents: truetrongnext.config.tsvà xóa các flagexperimental.pprhoặcexperimental.dynamicIO - Audit mọi page: thêm
"use cache"cho các trang tĩnh và data-fetching functions phục vụ nội dung công khai - Wrap tất cả dynamic content (user-specific, request-time) trong
<Suspense>boundaries với skeleton fallbacks có ý nghĩa - Sử dụng
"use cache: private"cho mọi function truy cập cookies, headers hoặc trả về dữ liệu cá nhân - Định nghĩa custom
cacheLifeprofiles cho các danh mục dữ liệu phổ biến (product data, user sessions, static content) - Thêm
cacheTag()vào mọi cached function có thể cần invalidation theo yêu cầu - Test trong production mode với
next build && next startvì caching behavior trongnext devkhác biệt đáng kể - Monitor cache-hit ratios theo page trong 24 giờ trước khi triển khai rộng
Bắt đầu luyện tập!
Kiểm tra kiến thức với mô phỏng phỏng vấn và bài kiểm tra kỹ thuật.
Kết luận
- Next.js 16 thay thế implicit caching bằng
"use cache"tường minh ở cấp độ file, component và function - PPR đã stable và mặc định dưới
cacheComponents: true, cung cấp static shells với dynamic content được stream cacheLifeprofiles thay thếrevalidatevới kiểm soát thời gian cache ba chiều được tập trungcacheTag+revalidateTagcho phép invalidation theo yêu cầu; thiếu tags đồng nghĩa chỉ có time-based expiry"use cache: private"là bắt buộc cho dữ liệu user-specific để ngăn chặn data leaks giữa users- Câu hỏi phỏng vấn năm 2026 tập trung vào sự chuyển đổi implicit-to-explicit, luồng rendering PPR, bảo mật cache và các pitfalls khi migration từ Next.js 15
Thẻ
Chia sẻ
Bài viết liên quan

React Compiler 2026: Tự động Memoization và Câu hỏi Phỏng vấn
Tìm hiểu React Compiler với tự động memoization, pipeline biên dịch HIR, các quy tắc React và câu hỏi phỏng vấn thường gặp năm 2026.

React 19 useEffectEvent và Activity: API mới và câu hỏi phỏng vấn 2026
Phân tích chi tiết useEffectEvent và Activity trong React 19.2. Giải pháp stale closure, pre-rendering nền, ví dụ mã nguồn và câu hỏi phỏng vấn thường gặp.

React 19: Server Components trong Production - Hướng dẫn đầy đủ
Làm chủ React 19 Server Components trong môi trường production. Kiến trúc, pattern, streaming, caching và tối ưu hóa cho ứng dụng hiệu suất cao.