NAME
nextjs-16-cache-components — Next.js 16، Cache Components و React Compiler: آنچه واقعاً تغییر می کند
SYNOPSIS
cat nextjs-16-cache-components.md
DESCRIPTION
برای سالها یکی از آزاردهندهترین سوالات در Next.js این بود: "این صفحه ثابت است یا پویا؟". این یک سوال ساده به نظر می رسد، تا زمانی که یک تماس به cookies()، یک fetch با گزینه های مختلف، یک سرویس گیرنده پایگاه داده، یک CMS، یک سبد خرید، یا یک قطعه از محتوای سفارشی اضافه کنید.
Next.js 16 جالب است زیرا سعی می کند این مکالمه را کمتر مرموز کند. این پیچیدگی را از بین نمیبرد، اما مدل ذهنی را تغییر میدهد: مسیرها بهطور پیشفرض پویا هستند، حافظه پنهان در جایی که لازم است خود را اعلام میکند، و Suspense راهی طبیعی برای ترکیب پوستههای سریع با قطعاتی میشود که تازه میمانند.
ویژگی قابل درک، Cache Components است. Stable Turbopack، React Compiler، proxy.ts، و API های جدید باطل اهمیت دارند، اما حول همین مشکل می چرخند: ساخت برنامه های سریع بدون نیاز به حدس زدن چارچوب در پشت صحنه چه تصمیمی گرفته است.
چون این چیز مهمه
در یک برنامه واقعی، شما فقط "صفحات ثابت" و "صفحات پویا" ندارید. شما قطعات مختلف با نیازهای متفاوت دارید.
برگه محصول ممکن است چند بار در روز تغییر کند. قیمت ممکن است بیشتر تغییر کند. در دسترس بودن باید تقریباً زنده باشد. نام کاربری شخصی است. نظرات را می توان به صورت جریانی پخش کرد. نوار کناری می تواند پایدار باشد. سبد خرید ندارد.
اگر همه چیز را به عنوان یک واحد در نظر بگیرید، همیشه در یکی از دو حالت افراطی قرار می گیرید:
- حافظه پنهان تهاجمی و خطر دیدن داده های قدیمی؛
- رندر پویا در همه جا و عملکرد بدتر از حد لازم.
Cache Components دقیقاً برای جلوگیری از این انتخاب اشتباه عمل می کند.
مدل در عمل
با cacheComponents: true، می توانید با استفاده از "use cache" آنچه را که در حافظه پنهان است، اعلام کنید. سپس می توانید مدت زمان و برچسب ها را با cacheLife() و cacheTag() مرتبط کنید. قطعات دینامیک پویا باقی می مانند و با Suspense قابل جداسازی هستند.
تنظیمات کوچک است:
// next.config.ts import type { NextConfig } from 'next'; const nextConfig: NextConfig = { cacheComponents: true, }; export default nextConfig;
تغییر بزرگ در پیکربندی نیست. این در نحوه شروع نوشتن اجزاست.
// app/products/[slug]/page.tsx import { Suspense } from 'react'; import { cacheLife, cacheTag } from 'next/cache'; async function getProduct(slug: string) { 'use cache'; cacheLife('hours'); cacheTag(`product:${slug}`); return db.product.findUnique({ where: { slug } }); } async function ProductDetails({ slug }: { slug: string }) { const product = await getProduct(slug); return ( <section> <h1>{product.name}</h1> <p>{product.description}</p> </section> ); } async function LiveInventory({ slug }: { slug: string }) { const inventory = await db.inventory.findFirst({ where: { slug } }); return <p>{inventory.quantity} pezzi disponibili</p>; } export default async function ProductPage({ params }: { params: Promise<{ slug: string }> }) { const { slug } = await params; return ( <> <ProductDetails slug={slug} /> <Suspense fallback={<p>Controllo disponibilità...</p>}> <LiveInventory slug={slug} /> </Suspense> </> ); }
لازم نیست صفحه تماماً کش یا تماماً پویا باشد. برگه محصول می تواند سریع و قابل استفاده مجدد باشد. موجودی می تواند تازه بماند. کاربر بلافاصله چیزی را می بیند، بدون اینکه منتظر کندترین قسمت باشد.
use cache مستندات اجرایی است
چیزی که من در مورد "use cache" دوست دارم این است که شما را مجبور می کند که قصدی را صریح بیان کنید. هنگامی که یک تابع را می خوانید، بلافاصله متوجه می شوید که شخصی تصمیم گرفته است: "این داده ها قابل استفاده مجدد هستند".
به خصوص زمانی که از fetch استفاده نمی کنید مفید است. بسیاری از برنامه ها داده های Prisma، Drizzle، SDK های داخلی، کلاینت های CMS یا توابع سرویس را می خوانند. در آن موارد، استدلال قدیمی فقط بر اساس گزینه های fetch کافی نبود.
یک قانون سرانگشتی:
- محتوای cachea نسبتاً پایدار است.
- از برچسب های دانه ای استفاده کنید.
- مجوزهای پویا، جلسات، سبد خریدها، اعلانها و وضعیتهای تراکنش را ترک میکند.
- قطعات کند را داخل
Suspenseقرار دهید. - قبل از گفتن "ما عملکرد را بهبود بخشیم" اندازه گیری کنید.
بدون دور ریختن همه چیز باطل کنید
کش تنها زمانی مفید است که بتوانید آن را به طور دقیق به روز کنید. در اینجا cacheTag، revalidateTag و updateTag مهم می شوند.
مثال:
'use server'; import { updateTag } from 'next/cache'; export async function updateProductName(productId: string, name: string) { await db.product.update({ where: { id: productId }, data: { name }, }); updateTag(`product:${productId}`); }
جزئیات مهم برچسب است. product:${productId} یک مرز دقیق را نشان می دهد. products به یک سطل بزرگ می گوید. در ابتدا سطل بزرگ راحت است. بعد از چند ماه این دلیلی می شود که نیمی از یک برنامه را برای تغییر عنوان باطل کنید.
توربوپک پایدار: بخشی که هر روز می شنوید
Next.js 16 Turbopack را برای توسعه و ساخت به مرکز می آورد. این شاعرانهترین ویژگی نیست، اما آن چیزی است که در حین کار احساس میکنید: سروری که زودتر شروع به کار میکند، سریعتر بهروزرسانی میشود، ساختهایی که احساس یک استراحت اجباری قهوه را ندارند.
با این حال، من یک پایگاه کد پر از پلاگین های سفارشی را با چشمان بسته منتقل نمی کنم. من بررسی می کنم:
- ساخت محلی؛
- واردات غیر استاندارد؛
- MDX، SVG و CSS؛
- افزونه های Webpack باقی مانده است.
- صفحات انتقادی؛
- تفاوت در زمان ساخت
برای پروژه های جدید، از حالت پیش فرض شروع می کنم. برای افراد بالغ، من یک مهاجرت سنجیده انجام می دهم.
React Compiler: حذف نویز، نه فکر
React Compiler 1.0 پایدار است و Next.js 16 آن را با reactCompiler پشتیبانی می کند. وعده کاهش مقدار زیادی از یادداشت دستی است: کمتر memo، کمتر useMemo، کمتر useCallback استفاده شده "برای ایمنی".
// next.config.ts import type { NextConfig } from 'next'; const nextConfig: NextConfig = { reactCompiler: true, }; export default nextConfig;
من با آن مانند یک گرد و غبار جادویی رفتار نمی کنم. کامپایلر زمانی کمک می کند که کد به خوبی از قوانین React پیروی کند. اگر اجزاء دارای عوارض جانبی عجیب، جهش های پنهان یا قلاب های بد استفاده شده باشند، ابتدا باید اصلاح شوند.
روش سالم امتحان کردن:
- به روز رسانی
eslint-plugin-react-hooks؛ - رفع تخلفات واقعی؛
- آن را در یک منطقه کنترل شده فعال کنید.
- اندازه گیری زمان ساخت و رفتار.
- ذخیره سازی دستی را فقط زمانی حذف کنید که دیگر مورد نیاز نباشد.
هدف پاک کردن هر useMemo نیست. هدف این است که نوشتن یادداشت پیشگیرانه را متوقف کنیم زیرا از رندر می ترسیم.
proxy.ts و مرز شبکه
middleware.ts قدیمی به proxy.ts تبدیل می شود. این یک تغییر نام است، اما منطقی است: آن فایل در مرز درخواست قرار میگیرد، یک میانافزار عمومی سنتی به سبک باطن نیست.
// proxy.ts import { NextRequest, NextResponse } from 'next/server'; export default function proxy(request: NextRequest) { const isLoggedIn = Boolean(request.cookies.get('session')); if (!isLoggedIn && request.nextUrl.pathname.startsWith('/dashboard')) { return NextResponse.redirect(new URL('/login', request.url)); } return NextResponse.next(); }
قانون اینجا ساده است: آن را کوچک نگه دارید. تغییر مسیر، مسیریابی احراز هویت، هدرها، بازنویسی های ضروری. اگر شروع به احساس یک باطن دوم کند، احتمالاً کار زیادی انجام می دهد.
واقعا چقدر مهاجرت میکردم
من همه ویژگی ها را به یکباره روشن نمی کنم. من این کار را انجام می دهم:
- من Next، React و React DOM را به روز می کنم.
- من کد مد رسمی را راه اندازی می کنم.
- من تغییرات شکستن را در
params،searchParams،cookies()،headers()وdraftMode()اصلاح می کنم. - من
middleware.tsرا بهproxy.tsمهاجرت می کنم. - من ساخت و صفحات بحرانی را بررسی می کنم.
- من Cache Components را در بخشی فعال می کنم که کش در حال حاضر اصطکاک ایجاد می کند.
- من قراردادهایی را برای برچسب ها و باطل کردن تعریف می کنم.
- من React Compiler را جداگانه امتحان می کنم.
- مقایسه معیارهای قبل و بعد
مهاجرت خوب آن چیزی نیست که از همه ویژگی های جدید استفاده کند. این چیزی است که رفتار برنامه را خواناتر می کند.
آنچه در طرز فکر تغییر می کند
مفیدترین چیز در مورد Next.js 16 این است که شما را مجبور می کند تا اهداف را بهتر نامگذاری کنید. یک تابع فقط "دریافت محصول از پایگاه داده" نیست. این "محصول را دریافت کنید، می توانم آن را برای ساعت ها کش کنم، با این برچسب آن را باطل می کنم". کامپوننت فقط "رندر صفحه" نیست. این "این پوسته سریع است، این قطعه شخصی است، این جریان پخش می شود."
در ابتدا به نظر می رسد کار بیشتر است. سپس به نوعی آرامش تبدیل می شود. تصمیمات مربوط به عملکرد دیگر در ترکیبی از پیش فرض ها، اکتشافی و حافظه قبیله ای پنهان نیستند. آنها در کد هستند.
منابع مفید
METADATA
- date: 2026-05-24
- reading: 7 min
- author: Filippo Spinella
- tags: Next.js, React, Frontend, Performance