فرق SSR، SSG و ISR در نکست جی اس

۱ ماه پیش
۰ بازدید
۰ دیدگاه
زمان مطالعه: ۱۰ دقیقه
نمونه تصویر فرق SSR، SSG و ISR د...
SSR، SSG و ISR سه برچسب رقیب نیستند؛ سه نقطه روی طیف «تازگی داده، هزینه محاسبه و قابلیت کش» هستند.

انتخاب میان SSR، SSG و ISR در Next.js یک تصمیم صرفاً مربوط به سرعت صفحه نیست؛ بلکه مستقیماً معماری داده، هزینه زیرساخت، قابلیت کش‌شدن و مدل سئو را تعیین می‌کند. در SPA کلاسیک، مرورگر ابتدا پوسته‌ای کم‌محتوا دریافت می‌کند و سپس با اجرای JavaScript داده را می‌گیرد و رابط کاربری را می‌سازد. این مدل برای داشبوردهای تعاملی مناسب است، اما برای اولین نمایش محتوا، خزیدن موتورهای جست‌وجو و شبکه‌های کند، هزینه دارد.

نگاهی به نکست جی اس

Next.js این مرز را با رندرینگ ترکیبی جابه‌جا می‌کند: هر مسیر می‌تواند بر اساس ماهیت داده و حساسیت زمانی آن، در زمان بیلد، در زمان درخواست یا به‌صورت استاتیکِ قابل‌بازاعتبارسنجی تولید شود. در معماری جدید App Router، این انتخاب‌ها بیش از آن‌که به چند تابع اختصاصی وابسته باشند، از ترکیب Server Components، رفتار fetch و سیاست‌های کش حاصل می‌شوند. مستندات رسمی Next.js نیز مهاجرت از getStaticProps و getServerSideProps به APIهای جدید App Router را توضیح می‌دهد؛ بااین‌حال Pages Router همچنان برای پروژه‌های موجود معتبر است مستندات رسمی Next.js.

نکته کلیدی این است که «استاتیک» الزاماً به معنای «داده قدیمی» و «داینامیک» الزاماً به معنای «بدون کش» نیست. هدف طراحی، قراردادن هر قطعه داده در ارزان‌ترین و سریع‌ترین نقطه‌ای است که نیاز تازگی آن را برآورده می‌کند.

1787659948_desc-ssr-ssg-isr-for-nextjs.jpg  

معماری و چرخه حیات روش های رندرینگ

  • CSR؛ نقطه مبدأ و هزینه انتقال مسئولیت به مرورگر

در Client-Side Rendering، سرور معمولاً HTML اولیه و فایل‌های JavaScript را تحویل می‌دهد؛ سپس مرورگر hydrate می‌کند، API را فراخوانی می‌کند و رابط کاربری را می‌سازد. چرخه به‌صورت «درخواست سند ← دریافت JavaScript ← اجرای کد ← دریافت داده ← رندر رابط» پیش می‌رود. مزیت اصلی، مناسب‌بودن برای تعاملات سنگین و کاهش نیاز به رندر HTML در سرور است؛ اما زمان رسیدن محتوای معنادار و قابلیت مشاهده مستقیم محتوا برای خزنده، به اجرای موفق JavaScript وابسته می‌شود. CSR برای بخش‌های شخصی‌سازی‌شده پس از ورود، ابزارهای داخلی و داده‌ای که نباید در HTML عمومی قرار گیرد، انتخاب طبیعی‌تری است.

  • SSG؛ تولید یک‌باره و تحویل از نزدیک‌ترین نقطه

در Static Site Generation، HTML در زمان build تولید می‌شود و در زمان درخواست، سرور برنامه الزاماً درگیر اجرای منطق تولید صفحه نیست. در Pages Router، الگوی کلاسیک با getStaticProps و در مسیرهای پویا با getStaticPaths ساخته می‌شود. خروجی استاتیک را می‌توان روی CDN توزیع کرد؛ بنابراین TTFB معمولاً پایین است، ظرفیت مقیاس‌پذیری بالاست و هزینه هر درخواست به‌جای اجرای مداوم منطق سروری، عمدتاً به تحویل آبجکت کش‌شده محدود می‌شود.

چرخه SSG چنین است: «دریافت داده در build ← تولید HTML و assetها ← انتشار ← پاسخ از CDN». این مدل برای لندینگ، مستندات، وبلاگ و صفحات محصولی که تغییراتشان با انتشار کنترل‌شده انجام می‌شود، بسیار کارآمد است. نقطه ضعف آن، هزینه build و کهنه‌شدن داده بین دو انتشار است. اگر تعداد مسیرها بسیار زیاد باشد، تولید همه صفحات زمان build و مصرف منابع CI را افزایش می‌دهد؛ در این وضعیت، تولید انتخابی یا ISR مناسب‌تر است.

  • SSR؛ تولید HTML در هر درخواست

در Server-Side Rendering، صفحه هنگام درخواست کاربر روی سرور تولید می‌شود. در Pages Router، getServerSideProps روی سرور اجرا می‌شود و می‌تواند داده‌ای را که فقط در زمان درخواست معلوم است، مانند هدر authorization یا موقعیت جغرافیایی، به صفحه پاس دهد راهنمای getServerSideProps. چرخه معمول عبارت است از: «درخواست کاربر ← اجرای منطق صفحه ← فراخوانی پایگاه داده یا API ← تولید HTML ← ارسال پاسخ ← hydrate در مرورگر».

SSR برای داده لحظه‌ای، محتوای وابسته به کاربر و سئوی داینامیک مناسب است. بااین‌حال هر cache miss می‌تواند شامل زمان اجرای سرور، اتصال به منبع داده و صف پردازش باشد؛ در نتیجه TTFB و هزینه عملیاتی آن معمولاً از SSG بیشتر است. SSR را نباید فقط به‌دلیل نیاز به سئو انتخاب کرد؛ اگر داده عمومی و کم‌تغییر است، SSG یا ISR همان مزیت سئو را با هزینه کمتر فراهم می‌کند.

  • ISR؛ مرز میانی میان SSG و SSR

Incremental Static Regeneration صفحه را به‌صورت استاتیک تحویل می‌دهد، اما اجازه می‌دهد خروجی پس از یک بازه زمانی مشخص بازاعتبارسنجی شود. در Pages Router، مقدار revalidate در خروجی getStaticProps این سیاست را تعیین می‌کند. در مدل زمانی ساده، نخستین پاسخ پس از پایان عمر کش ممکن است نسخه قبلی را تحویل دهد و بازسازی در پس‌زمینه انجام شود؛ درخواست‌های بعدی نسخه تازه را می‌بینند. این رفتار شبیه الگوی stale-while-revalidate است، هرچند جزئیات دقیق به پلتفرم استقرار و لایه کش وابسته است راهنمای ISR در Next.js. این ISR چرخه build را از چرخه به‌روزرسانی جدا می‌کند: صفحه می‌تواند در ابتدا یا هنگام اولین تقاضا ساخته شود، سپس با TTL مشخص، بدون build کامل سایت بازتولید شود. مزیت آن، ترکیب cacheability استاتیک با تازگی دوره‌ای است. چالش‌ها شامل درک وضعیت نسخه قدیمی، هماهنگی invalidation میان نودها، رفتار مسیرهای تازه و جلوگیری از کش‌شدن ناخواسته داده شخصی است. برای داده‌هایی که تغییراتشان رویدادمحور است، بازاعتبارسنجی on-demand یا tag-based در App Router می‌تواند دقیق‌تر از TTL صرف باشد.

1787659743_info-ssr-ssg-isr-for-nextjs.jpg  

پیاده سازی در Pages Router و App Router

در Pages Router، مرزبندی APIها صریح است:

// pages/products/[slug].tsx
export async function getStaticProps({ params }) {
const product = await getProduct(params.slug)
return { props: { product }, revalidate: 60 }
}
export async function getStaticPaths() {
return { paths: [], fallback: 'blocking' }
}

این الگو برای صفحه‌ای است که HTML آن قابل اشتراک‌گذاری است و می‌تواند هر ۶۰ ثانیه بازاعتبارسنجی شود. برای SSR، همان صفحه به‌جای getStaticProps، getServerSideProps را export می‌کند و داده در هر درخواست خوانده می‌شود. استفاده هم‌زمان از این دو تابع در یک صفحه مجاز نیست. در fallback: 'blocking'، مسیر تولیدنشده نخستین‌بار با منطق سروری ساخته می‌شود و پس از آن می‌تواند مانند خروجی استاتیک استفاده شود؛ رفتار دقیق باید با نسخه Next.js و زیرساخت بررسی شود.

در App Router، فایل‌های داخل app به‌صورت پیش‌فرض Server Component هستند. بنابراین می‌توان دسترسی به منابع داده را نزدیک سرور نگه داشت و فقط بخش‌های واقعاً تعاملی را با 'use client' به کلاینت فرستاد. نمونه سیاست‌های رایج:

// app/blog/[slug]/page.tsx
export default async function Page({ params }) {
const { slug } = await params
const post = await fetch(`https://api.example.com/posts/${slug}`, {
  next: { revalidate: 300 },
}).then((res) => res.json())
return <article><h1>{post.title}</h1><p>{post.body}</p></article>
}

در اینجا next: { revalidate: 300 } داده را با عمر پنج‌دقیقه‌ای قابل بازاعتبارسنجی می‌کند. cache: 'force-cache' برای داده عمومی و نسبتاً پایدار، و cache: 'no-store' برای داده‌ای که باید در زمان درخواست خوانده شود مناسب است:

const catalog = await fetch(CATALOG_URL, { cache: 'force-cache' })
const account = await fetch(ACCOUNT_URL, { cache: 'no-store' })

این گزینه‌ها را باید در سطح هر منبع داده تحلیل کرد؛ زیرا یک صفحه می‌تواند هم‌زمان داده عمومی کش‌شده و داده خصوصی بدون کش داشته باشد. استفاده از کوکی، هدر authorization یا APIهای وابسته به درخواست نیز می‌تواند مسیر را پویا کند. مستندات مهاجرت رسمی تأکید می‌کند که App Router جایگزین مفهومی APIهای قدیمی داده‌گیری است، نه صرفاً تغییر نام آن‌ها راهنمای مهاجرت. در نسخه‌های جدید، جزئیات پیش‌فرض کش و Cache Components ممکن است تغییر کند؛ بنابراین قرارداد نسخه مستقرشده باید مرجع نهایی باشد مستندات Next.js.

کش در CDN و Edge و تعامل آن با ISR

رندرینگ فقط در لایه Next.js تصمیم‌گیری نمی‌شود؛ مسیر کامل شامل مرورگر، CDN، Edge، سرور Next.js و منبع داده است. اگر HTML عمومی با سیاست قابل‌کش ارسال شود، CDN می‌تواند آن را در نقاط نزدیک کاربر نگه دارد و درخواست‌های بعدی را بدون اجرای مجدد کد پاسخ دهد. اگر پاسخ شامل داده شخصی، کوکی حساس یا هدر authorization است، کش عمومی می‌تواند نشت اطلاعات ایجاد کند و باید از آن جلوگیری شود.

Cache-Control قرارداد میان origin و لایه‌های کش است. public امکان ذخیره عمومی را اعلام می‌کند؛ max-age عمر قابل‌استفاده در کش را مشخص می‌کند و s-maxage می‌تواند عمر متفاوتی برای shared cache تعیین کند. stale-while-revalidate اجازه می‌دهد کش در مدت مشخص نسخه موجود را تحویل دهد و هم‌زمان نسخه تازه را درخواست کند. این هدرها باید با مدل invalidation برنامه هماهنگ باشند؛ TTL طولانی همراه با نبود purge، داده قدیمی را طولانی می‌کند.

در ISR، یک cache key معمولاً به مسیر، پارامترها و گاهی وابستگی‌های درخواست مربوط است. CDN ممکن است خروجی را جلوتر از origin نگه دارد؛ در نتیجه تغییر فایل یا داده در origin تا زمان انقضای کش CDN دیده نشود. برای استقرار چندمنطقه‌ای، باید روشن باشد که کش ISR و وضعیت بازسازی در کجا نگهداری می‌شود و آیا نودها invalidation مشترک دارند. همچنین Edge Runtime الزاماً به معنای اجرای همه قابلیت‌های Node.js در لبه نیست؛ محدودیت‌های runtime و دسترسی به دیتابیس باید پیش از انتخاب معماری آزموده شود.

قاعده عملی این است: داده عمومی را با کش صریح و عمر قابل‌اندازه‌گیری منتشر کنید، داده خصوصی را no-store یا با کش خصوصی نگه دارید، و برای تغییرات مهم از invalidation رویدادمحور استفاده کنید. در Pages Router، راهنمای رسمی ISR بر استاتیک‌بودن خروجی و بازتولید تدریجی تأکید دارد؛ در App Router، سیاست‌های fetch و APIهای revalidation ابزار کنترل دانه‌ریزتری فراهم می‌کنند راهنمای ISR.

راهنمای تصمیم گیری معماری بر اساس نوع محصول

انتخاب راهبرد را از «نیاز داده» شروع کنید، نه از نام تکنیک. اگر صفحه باید برای هر کاربر بر اساس نشست، مجوز، موقعیت یا زمان دقیق متفاوت باشد، SSR یا ترکیب Server Component با no-store را بررسی کنید. اگر داده عمومی است، ابتدا SSG را فرض پایه بگیرید؛ فقط وقتی تازگی دوره‌ای لازم است به ISR بروید و وقتی تغییر باید تقریباً در همان درخواست دیده شود، SSR را انتخاب کنید.

درخت تصمیم فشرده

۱. آیا HTML به داده خصوصی یا context درخواست وابسته است؟ اگر بله، SSR یا رندر سروری بدون کش عمومی؛ اگر نه، به گام بعد بروید.

۲. آیا داده فقط هنگام انتشار تغییر می‌کند؟ اگر بله، SSG؛ اگر نه، به گام بعد بروید.

۳. آیا تأخیر چندثانیه‌ای تا چنددقیقه‌ای قابل‌قبول است؟ اگر بله، ISR با TTL یا invalidation رویدادمحور؛ اگر نه، SSR یا کش داده با سیاست دقیق.

۴. آیا صفحه عمدتاً تعاملی و پشت احراز هویت است؟ پوسته سروری سبک و CSR برای ویجت‌های تعاملی معمولاً مناسب‌تر از SSR کامل است.

برای وبلاگ، SSG برای نوشته‌های منتشرشده و ISR برای ویرایش‌های پرتکرار یا فهرست مطالب مناسب است؛ بازاعتبارسنجی on-demand پس از انتشار CMS از انتظار برای پایان TTL بهتر است. برای فروشگاه، صفحات محصول و دسته‌بندی معمولاً عمومی‌اند و ISR تعادل خوبی میان تازگی قیمت/موجودی و کش CDN ایجاد می‌کند؛ سبد خرید، قیمت شخصی‌سازی‌شده و حساب کاربری نباید در HTML کش عمومی قرار گیرند. برای داشبورد، داده وابسته به کاربر و به‌روزرسانی لحظه‌ای، SSR برای shell یا CSR پس از ورود مناسب است؛ نمودارهای تعاملی را بی‌دلیل روی سرور رندر نکنید. برای لندینگ، SSG انتخاب پیش‌فرض است و فقط بخش‌هایی مانند فرم یا آزمایش شخصی‌سازی‌شده باید داینامیک شوند.

پیش از نهایی‌کردن تصمیم، چهار سنجه را در محیط نزدیک به تولید اندازه بگیرید: TTFB در cache hit و miss، زمان build، نرخ خطای منبع داده و درصد درخواست‌هایی که واقعاً به origin می‌رسند. همچنین سیاست purge، نسخه‌گذاری API و مسیر بازگشت در زمان خطای بازسازی را مستند کنید.

نتیجه گیری

SSR، SSG و ISR سه برچسب رقیب نیستند؛ سه نقطه روی طیف «تازگی داده، هزینه محاسبه و قابلیت کش» هستند. SSG کم‌هزینه‌ترین و قابل‌مقیاس‌ترین گزینه برای HTML عمومی و پایدار است. SSR زمانی ارزش دارد که پاسخ واقعاً به context درخواست یا داده لحظه‌ای وابسته باشد. ISR برای بیشتر کاتالوگ‌ها، وبلاگ‌های پویا و صفحات عمومی پرتغییر، سازش مهندسی مطلوبی فراهم می‌کند: تحویل استاتیک در مسیر معمول و بازسازی کنترل‌شده در پس‌زمینه.

در Pages Router، این تصمیم با getStaticProps، getServerSideProps و revalidate صریح‌تر دیده می‌شود. در App Router، ترکیب Server Components و سیاست‌های fetch مانند force-cache، no-store و next.revalidate امکان می‌دهد سیاست کش برای هر منبع داده جداگانه تعریف شود. نتیجه مطلوب فقط با انتخاب API به‌دست نمی‌آید؛ باید cache key، هدرهای HTTP، رفتار CDN، invalidation و حساسیت داده نیز هم‌زمان طراحی شوند. مستندات رسمی Next.js استفاده از SSR را برای داده شخصی یا اطلاعاتی که فقط در زمان درخواست معلوم است توصیه می‌کند و برای داده قابل‌کش یا قابل‌پیش‌رندر، الگوی استاتیک را ترجیح می‌دهد راهنمای SSR.

این مقاله بر پایه مستندات رسمی Next.js تنظیم شده است. اعداد عملکردی ثابت ارائه نشده‌اند، زیرا TTFB، زمان build و نرخ hit کش به کد، داده، منطقه استقرار و پلتفرم اجرا وابسته‌اند.

مقالات مرتبط