Next.js
۷ دقیقه مطالعهانتخاب روش رندرینگ مناسب در Next.js
یک چارچوب عملی برای انتخاب رندر استاتیک، رندر داینامیک سمت سرور، بازاعتبارسنجی و تعاملات سمت کلاینت در App Router.

رندرینگ برای هر مسیر و گاهی برای هر منبع داده تصمیم جداگانهای است. خروجی استاتیک برای محتوای عمومی عالی است، رندر زمان درخواست برای پاسخ شخصیسازیشده لازم میشود، ISR بین تازگی و استفاده مجدد تعادل ایجاد میکند و دریافت سمت کلاینت برای تعاملات وابسته به مرورگر مناسب است. مثالها الگو هستند، نه تنظیماتی برای کپی کورکورانه؛ رفتار نسخه Next.js پروژه را بررسی کن.
۱. تولید استاتیک (SSG)
برای صفحاتی که محتوایشان پیش از درخواست قابل آمادهسازی است از SSG استفاده کن؛ مثل درباره ما، مستندات، پورتفولیو یا مقاله منتشرشده. این روش کار سرور در هر درخواست را کاهش میدهد و امکان ارائه از CDN را فراهم میکند. در App Router صفحهای که فقط داده عمومی دارد و از APIهای زمان درخواست استفاده نمیکند اغلب بهصورت استاتیک رندر میشود؛ force-static گزینهای صریح است، نه کلید جادویی افزایش سرعت.
// Public content that changes only when you publish a new build
import { posts } from "@/blog/data/posts";
export const dynamic = "force-static";
export default function BlogPage() {
return <BlogList posts={posts} />;
}۲. بازتولید استاتیک تدریجی (ISR)
ISR وقتی مفید است که داده عمومی تغییر میکند اما لازم نیست برای هر بازدیدکننده از مبدأ دوباره رندر شود؛ مثل فهرست کالا، جزئیات عمومی محصول یا خبرها. revalidate = 300 پنجره تازگی پنجدقیقهای را بیان میکند، البته رفتار دقیق به نسخه فریمورک و پلتفرم استقرار وابسته است. برای انتشار محتوا، invalidation بر اساس tag یا path میتواند از انتظار برای پایان بازه دقیقتر باشد.
// Reuse the generated response and refresh it at most every 5 minutes
export const revalidate = 300;
export default async function ProductsPage() {
const products = await getPublicProducts();
return <ProductGrid products={products} />;
}۳. رندر داینامیک سمت سرور (SSR)
وقتی خروجی به session، کوکی، header یا اطلاعات مختص درخواست وابسته است، رندر زمان درخواست مناسب است. مثال session را از کوکی میخواند و حساب همان کاربر را دریافت میکند. HTML یا داده شخصیسازیشده را در کش مشترک ذخیره نکن. همچنین رندر داینامیک جای بررسی مجوز دسترسی در هر عملیات حساس داده را نمیگیرد.
import { cookies } from "next/headers";
export const dynamic = "force-dynamic";
export default async function AccountPage() {
const cookieStore = await cookies();
const session = await readSession(cookieStore);
if (!session) redirect("/login");
const account = await getAccount(session.userId);
return <AccountDashboard account={account} />;
}۴. رندر و دریافت داده سمت کلاینت (CSR)
دریافت سمت کلاینت برای داده پرتغییر یا دادهای که بعد از بارگذاری صفحه و در واکنش به تعامل کاربر لازم است مفید است؛ مثل قیمت زنده یا فیلتر قابلکنترل. مرز کلاینت را کوچک نگه دار، وضعیت loading و خطا داشته باش، درخواستهای منسوخ را لغو کن و اطلاعات محرمانه سرور را در مرورگر قرار نده. در برنامه واقعی، کتابخانه دریافت داده میتواند کش، حذف درخواست تکراری، retry و revalidation هنگام focus را بهتر از effectهای پراکنده مدیریت کند.
"use client";
import { useEffect, useState } from "react";
type Quote = { symbol: string; price: number };
export function LiveStock({ symbol }: { symbol: string }) {
const [quote, setQuote] = useState<Quote | null>(null);
const [error, setError] = useState(false);
useEffect(() => {
const controller = new AbortController();
fetch(`/api/quotes/${symbol}`, { signal: controller.signal })
.then((r) => { if (!r.ok) throw new Error(); return r.json(); })
.then(setQuote)
.catch((e) => { if (e.name !== "AbortError") setError(true); });
return () => controller.abort();
}, [symbol]);
if (error) return <p role="alert">Quote unavailable.</p>;
if (!quote) return <p aria-live="polite">Loading quote…</p>;
return <p>{quote.symbol}: {quote.price}</p>;
}۵. پیشرندر مسیرهای پویا با generateStaticParams
برای slugهای عمومی و شناختهشده، generateStaticParams به build اجازه میدهد خروجی مسیرها را از قبل آماده کند. dynamicParams = false باعث میشود slug ناشناخته 404 شود، نه اینکه هنگام درخواست ساخته شود. برای مجموعه نامحدود یا بسیار پرتغییر، زمان build و سیاست revalidation را در نظر بگیر.
import { posts } from "@/blog/data/posts";
export function generateStaticParams() {
return posts.map((post) => ({ slug: post.slug }));
}
export const dynamicParams = false; // 404 for slugs not generated above۶. بازاعتبارسنجی پس از تغییر داده
وقتی مدیر محصولی را منتشر میکند، انتظار برای پایان پنجره زمانی ممکن است خروجی قدیمی را نگه دارد. پس از موفقیت نوشتن دیتابیس، tag و/یا مسیر مربوط را invalid کن. tag باید با tag استفادهشده در دریافت داده یکی باشد و امضای API را با نسخه Next.js بررسی کن؛ APIهای کش در نسخههای مختلف تغییر کردهاند.
"use server";
import { revalidatePath, revalidateTag } from "next/cache";
export async function publishProduct(id: string) {
await updateProductInDatabase(id);
revalidateTag("products");
revalidatePath("/products");
}۷. بر اساس نوع صفحه تصمیم بگیر
- پورتفولیو، مستندات و مقاله منتشرشده ← SSG؛ اگر محتوا بدون build تغییر میکند از revalidation استفاده کن.
- کاتالوگ عمومی با تغییر دورهای ← ISR یا revalidation بر اساس tag/path.
- حساب کاربری، پرداخت و داده مدیر ← رندر زمان درخواست همراه با مجوزسنجی سمت سرور؛ خروجی خصوصی را در کش مشترک نگذار.
- فیلتر زنده، نمودار و ابزارک پرتغییر ← Client Component کوچک، معمولاً همراه کتابخانه query.
۸. در build نهایی بررسی کن
از next build استفاده کن و خلاصه خروجی مسیرها را ببین؛ فقط بهخاطر سادهبودن کد فرض نکن صفحه استاتیک است. با داده واقعی، احراز هویت، سرویس کند و محیط مستقرشده آزمایش کن. رندرینگ مصالحهای بین تازگی، تأخیر، هزینه سرور، امنیت کش و تجربه کاربر است؛ رقابت برای استاتیککردن همه مسیرها نیست.