هنگام استفاده از کامپوننتهای کش (Cache Components)، فریمورک Next.js از معماری هوشمندی به نام Partial Prerendering (PPR) به صورت پیشفرض استفاده میکند. در این لایه، ساختار کامپوننتهای شما در زمان بیلد (Build time) ارزیابی شده و بر اساس ابزارها و APIهایی که به کار بردهاید، به شکلهای متفاوتی مدیریت میشوند.
ساختار و اجزای تشکیلدهنده پوسته استاتیک (Static Shell)
تفاوت فرآیند پردازش کامپوننتها در زمان بیلد (Build time) و زمان درخواست (Request time)
چگونگی انصراف (Opting out) از پوسته استاتیک روی کل پروژه
بررسی یک سناریوی جامع: ترکیب محتوای استاتیک، کَش داینامیک و استریم زنده
در زمان بیلد، Next.js درخت کامپوننتهای مسیر شما را رندر میکند. رفتار ریآکت با هر بخش بر اساس سه فاکتور تعیین میشود:
۱. دایرکتیو "use cache": خروجی این توابع یا کامپوننتها فوراً کَش شده و مستقیماً داخل پوسته استاتیک تزریق میشود. ۲. تگ <Suspense>: لایه فرعی یا همان رابط کاربری انتظار (fallback UI) به عنوان یک جایگاه موقت در پوسته استاتیک قرار میگیرد تا دیتای اصلی در زمان درخواست کاربر استریم شود. ۳. عملیاتهای معین (Deterministic): محاسبات خالص ریاضی و لود ماژولها نیز مستقیماً پردازش شده و به پوسته استاتیک اضافه میشوند.
خروجی این فرآیند، یک پوسته استاتیک (Static Shell) است که شامل کدهای HTML (برای لود اولیه و آنی صفحه) و یک دیتاست سریالسازیشده به نام RSC Payload (برای مسیریابی سریع سمت کلاینت بدون رفرش) میباشد.
⚠️ خطای عدم مدیریت دادهها: Next.js از شما میخواهد وضعیت تمام بخشهایی که نمیتوانند در زمان بیلد کامل شوند را مشخص کنید. اگر کامپوننتی دیتای زمان اجرا را بخواند اما درون تگ
<Suspense>قرار نگرفته باشد یا مارک"use cache"نداشته باشد، با خطای زیر در محیط توسعه و بیلد مواجه خواهید شد:Uncached data was accessed outside of <Suspense>
اگر تمایل دارید به هر دلیلی کل برنامه یا یک مسیر خاص کاملاً داینامیک رفتار کند و هیچ پوسته استاتیکی از قبل ساخته نشود، میتوانید یک تگ <Suspense> با مقدار fallback={null} را در بالاترین سطح لایوت ریشه (Root Layout)، درست بالای تگ <body> قرار دهید.
چون هیچ قالب یا لودینگی برای فرستادن وجود ندارد، Next.js ارسال پاسخ به مرورگر را تا زمان رندر کامل صفحه روی سرور مسدود (Block) میکند:
// app/layout.tsx
import { Suspense } from 'react'
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html>
{/* پاس دادن null باعث به تعویق افتادن کل رندر تا زمان درخواست کاربر میشود */}
<Suspense fallback={null}>
<body>{children}</body>
</Suspense>
</html>
)
}
برای درک کامل این که چطور محتوای استاتیک، کَش داینامیک و دیتای زنده زمان اجرا در یک صفحه در کنار هم کار میکنند، به این مثال کاربردی از یک صفحه وبلاگ نگاه کنید:
// app/blog/page.tsx
import { Suspense } from 'react'
import { cookies } from 'next/headers'
import { cacheLife, cacheTag, updateTag } from 'next/cache'
import Link from 'next/link'
export default function BlogPage() {
return (
<>
{/* ۱. محتوای استاتیک - به صورت خودکار پیشرندر میشود */}
<header>
<h1>وبلاگ ما</h1>
<nav>
<Link href="/">خانه</Link> | <Link href="/about">درباره ما</Link>
</nav>
</header>
{/* ۲. محتوای داینامیک کَششده - در پوسته استاتیک جای میگیرد */}
<BlogPosts />
{/* ۳. محتوای زمان اجرا - در زمان درخواست کاربر استریم میشود */}
<Suspense fallback={<p>در حال بارگذاری تنظیمات شما...</p>}>
<UserPreferences />
</Suspense>
{/* ۴. اکشن سرور برای ثبت پست و انقضای کش */}
<Suspense fallback={<p>Loading...</p>}>
<CreatePost />
</Suspense>
</>
)
}
// نمایش پستها (مشترک برای همه، آپدیت در هر ساعت)
async function BlogPosts() {
'use cache'
cacheLife('hours')
cacheTag('posts') // ثبت تگ برای مدیریت انقضای کش
const res = await fetch('https://api.vercel.app/blog')
const posts = await res.json()
return (
<section>
<h2>آخرین پستها</h2>
<ul>
{posts.slice(0, 5).map((post: any) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
</section>
)
}
// شخصیسازی شده بر اساس کوکی مرورگر کاربر (کامپوننت داینامیک)
async function UserPreferences() {
const theme = (await cookies()).get('theme')?.value || 'light'
return <aside><p>پوسته فعلی شما: {theme}</p></aside>
}
// فرم ایجاد پست مخصوص ادمین (Mutation)
async function CreatePost() {
const isAdmin = (await cookies()).get('role')?.value === 'admin'
if (!isAdmin) return null
async function createPost(formData: FormData) {
'use server'
// ذخیره در دیتابیس...
// منقضی کردن فوری کشِ پستها در سراسر سرور
updateTag('posts')
}
return (
<form action={createPost}>
<input name="title" placeholder="عنوان پست" required />
<button type="submit">انتشار</button>
</form>
)
}
در مرحله پیشرندرینگ (Build Time): بخش <header> (کامپوننت استاتیک) به همراه خروجی کامپوننت <BlogPosts /> (چون دستور "use cache" دارد) پردازش شده و به همراه تگ لودینگِ کامپوننت UserPreferences درون پوسته استاتیک (Static Shell) ذخیره میشوند.
در لحظه ورود کاربر (Request Time): پوسته استاتیک (هدر + لیست مقالات کَش شده + انیمیشن لودینگ) فوراً روی مرورگر کاربر رندر میشود. همزمان در پسزمینه، سرور کوکی کاربر را میخواند و بخش شخصیسازی شدهی UserPreferences را پردازش کرده و روی صفحه استریم میکند.
در زمان ارسال فرم (Mutation): وقتی ادمین پست جدیدی منتشر میکند، متد updateTag('posts') اجرا میشود. این دستور کشِ مربوط به کامپوننت BlogPosts را درجا منقضی میکند تا کاربر بعدی که وارد صفحه میشود، جدیدترین لیست پستها را مشاهده کند.
رندرینگ پیشفرض در معماری Cache Components بر پایه Partial Prerendering (PPR) استوار است.
پوسته استاتیک شامل بخشهای ۱۰۰٪ ثابت و خروجی کامپوننتهای مجهز به "use cache" است.
کامپوننتهای وابسته به دیتای زنده باید حتماً در <Suspense> قرار گیرند تا فاز لود اولیه را مسدود نکنند.
با قرار دادن تگ <Suspense fallback={null}> در لایوت ریشه، میتوان کل ساختار کَش استاتیک بیلد را غیرفعال و برنامه را کاملاً داینامیک کرد.
متد updateTag() به شما اجازه میدهد تا کش یک بخش خاص از پوسته استاتیک را پس از ثبت دیتای جدید فوراً مَنیپکآوت (Expire) کنید.
این محتوا کاملا رایگان توسط تیم کدلپر ترجمه شده و در اختیار شما کاربران عزیز قرار گرفته است، هر گونه کپی برداری برای مقاصد غیر رایگان و بدون ذکر منبع، مورد پیگیری قانونی قرار میگیرد.
ترجمه شده از منبع: https://nextjs.org/docs/app