کالبدشکافی React Server Components در Next.js
React Server Components فقط یک قابلیت performance نیست؛ مدل ذهنی تازهای برای تقسیم کار بین سرور و مرورگر است.
کالبدشکافی React Server Components در Next.js
React Server Components یا RSC یکی از مهمترین تغییرهای React و Next.js در سالهای اخیر است. اگر آن را فقط به عنوان راهی برای کم کردن JavaScript سمت کلاینت ببینیم، بخش بزرگی از ماجرا را از دست میدهیم. RSC بیشتر از آنکه یک ترفند performance باشد، یک مدل معماری است: بعضی کامپوننتها روی سرور اجرا میشوند، بعضی در مرورگر، و رابط بین این دو باید آگاهانه طراحی شود.
در Next.js App Router، Server Component پیشفرض است. این یعنی هر کامپوننت تا وقتی 'use client' نداشته باشد، در سمت سرور در نظر گرفته میشود.
Server Component چه کاری میکند؟
Server Component میتواند به منابع سروری دسترسی داشته باشد: دیتابیس، فایل، secret، یا API داخلی. خروجی آن به شکلی به React داده میشود که مرورگر لازم نیست کد آن کامپوننت را دانلود و اجرا کند.
این ویژگی چند نتیجه مهم دارد:
- حجم JavaScript مرورگر کمتر میشود.
- data fetching به منبع داده نزدیکتر میشود.
- secretها در سرور میمانند.
- صفحه میتواند سریعتر HTML معنیدار تولید کند.
اما یک محدودیت روشن هم وجود دارد: Server Component نمیتواند event handler، state مرورگر، یا useEffect داشته باشد.
Client Component چه زمانی لازم است؟
هر وقت کاربر باید با UI تعامل کند، احتمالاً به Client Component نیاز دارید. مثالها:
- باز و بسته کردن منو
- انتخاب tab
- فرم تعاملی
- drag and drop
- استفاده از
windowیاlocalStorage - subscription real-time در مرورگر
'use client'
export function ThemeToggle() {
return <button onClick={() => document.documentElement.classList.toggle('dark')}>Toggle theme</button>
}
نکته مهم این است که Client Component را تا جای ممکن پایین در درخت نگه دارید. اگر کل layout را client کنید، مقدار زیادی از مزیت RSC را از دست میدهید.
مرز سرور و کلاینت
مرز بین Server Component و Client Component همان جایی است که معماری برنامه مشخص میشود. یک Server Component میتواند داده را بگیرد و به Client Component props بدهد، اما Client Component نمیتواند مستقیم یک Server Component را import کند و مثل کامپوننت عادی اجرا کند.
الگوی سالم این است:
// Server Component
export default async function ProductPage() {
const product = await getProduct()
return <ProductDetails product={product} />
}
'use client'
export function ProductDetails({ product }: { product: Product }) {
return <button onClick={() => addToCart(product.id)}>افزودن به سبد</button>
}
داده روی سرور آماده میشود، تعامل در مرورگر انجام میشود.
اشتباه رایج: استفاده زیاد از use client
بسیاری از پروژهها وقتی با خطای مربوط به hook یا event handler مواجه میشوند، سریع 'use client' را بالای فایل میگذارند. این کار مشکل را موقت حل میکند، اما اگر زیاد تکرار شود، برنامه کمکم به همان مدل SPA قدیمی برمیگردد.
قبل از اضافه کردن 'use client' بپرسید:
- آیا این کامپوننت واقعاً state مرورگر دارد؟
- آیا فقط یک بخش کوچک آن تعاملی است؟
- آیا میتوانم بخش تعاملی را جدا کنم؟
- آیا دادهای که اینجا میگیرم باید روی سرور بماند؟
Streaming و Suspense
یکی از مزیتهای App Router این است که میتوانید بخشهایی از صفحه را زودتر نمایش دهید و بخشهای کندتر را بعداً stream کنید. Suspense در این مدل ابزار مهمی است.
import { Suspense } from 'react'
export default function DashboardPage() {
return (
<>
<Header />
<Suspense fallback={<MetricsSkeleton />}>
<Metrics />
</Suspense>
</>
)
}
این کار مخصوصاً در داشبوردها، گزارشها، و صفحههایی که چند منبع داده دارند مفید است.
جمعبندی
React Server Components از شما میخواهد دوباره درباره مسئولیت کامپوننتها فکر کنید. همه چیز نباید در مرورگر اجرا شود، و همه چیز هم نباید روی سرور بماند. معماری خوب یعنی مرز را درست انتخاب کنید: داده، امنیت، و render اولیه روی سرور؛ تعامل و state محلی در کلاینت.
منبع
- عنوان اصلی: The Forensics Of React Server Components
- منبع: Smashing Magazine
- این نوشته یک بازنویسی و ترجمه فارسی با اجازه انتشار است.