در حال دریافت اطلاعات ...
متن رفرنس
به طور خلاصه، در Next.js شما نمیتوانید یک Server Component را مستقیماً داخل یک Client Component ایمپورت (Import) کنید. اما میتوانید یک Server Component را به عنوان یک پراپ (معمولاً پراپ children) از طریق یک کامپوننت پدر (که روی سرور است) به یک Client Component پاس بدهید.
در مثال داکیومنت، <Modal> نیاز به تعامل کاربر دارد (مثلاً با کلیک باز و بسته شود)، پس باید use client باشد. اما <Cart> (سبد خرید) باید اطلاعات را از دیتابیس بخواند، پس بهتر است Server Component باشد. راه حل این است که در صفحه اصلی (Page)، سبد خرید را به عنوان فرزند (children) به مودال پاس بدهیم.
حفظ امنیت و کارایی سرور: اگر <Cart> را تبدیل به Client Component میکردیم، مجبور بودیم تمام لاجیک گرفتن دادهها (Data Fetching) را سمت مرورگر ببریم که هم نیاز به ساخت API داشت و هم ممکن بود کلیدهای امنیتی لو بروند.
کاهش حجم باندل جاوااسکریپت (Bundle Size): با این الگو، کدهای سنگین (مثل اتصال به دیتابیس یا کتابخانههای سنگین پردازش داده) که در <Cart> هستند، سمت سرور میمانند و به مرورگر کاربر ارسال نمیشوند. کاربر فقط کدهای JS مربوط به <Modal> را دانلود میکند.
جلوگیری از Client-side شدن کل اپلیکیشن: اگر این الگو نبود، به محض اینکه یک کامپوننت والد (مثل Layout یا Modal) نیاز به useState داشت، مجبور میشدیم تمام فرزندان آن را هم کلاینت کنیم که کل مزیت Server Components از بین میرفت.
آکاردئونها (Accordion) یا منوهای بازشو: خود آکاردئون برای باز و بسته شدن نیاز به use client دارد. اما محتوای داخل هر بخش آکاردئون میتواند یک مقاله سنگین خوانده شده از CMS باشد که به عنوان children (از نوع Server) به آن پاس داده میشود.
اسلایدرها و کاروسلها (Carousel): لاجیک کشیدن (Swipe)، دکمههای Next/Prev و انیمیشنها کلاینت هستند. اما خود اسلایدها (تصاویر و متنهایی که از دیتابیس میآیند) روی سرور رندر شده و به اسلایدر پاس داده میشوند.
نقشه تعاملی (Interactive Map): نقشه (Google Maps یا Leaflet) یک Client Component است که کاربر روی آن زوم میکند. اما مارکرها (Markers) یا پاپآپهای روی نقشه میتوانند کامپوننتهای سرور باشند که اطلاعاتشان را مستقیماً از دیتابیس خواندهاند.
هر زمان که یک پوسته یا ظرف (Container) تعاملی دارید که قرار است دادههای ثابت یا مبتنی بر سرور (Data-driven) را درون خود نمایش دهد. به طور کلی، هر وقت خواستید دور یک دیتای سروری، یک ویژگی تعاملی (مثل کلیک، هاور، انیمیشن) بپیچید، این بهترین الگوی ممکن است.
روال رندرینگ در این الگو بسیار جالب و هوشمندانه است (همانطور که در پاراگراف آخر داکیومنت نوشته شده):
رندر روی سرور: وقتی کاربر درخواستی میفرستد، Next.js شروع به رندر کردن کامپوننت <Page> روی سرور میکند.
تشخیص کلاینت و سرور: به <Modal> میرسد. میبیند use client است. پس میگوید: “من کدهای این را اجرا نمیکنم، به جایش یک جای خالی (Placeholder) میگذارم تا مرورگر بعداً آن را بسازد.”
رندر کامل فرزندان سروری: سپس به <Cart> میرسد (که به عنوان children پاس داده شده). با اینکه <Cart> داخل یک کامپوننت کلاینت است، Next.js آن را به طور کامل روی سرور رندر میکند (دیتا را میگیرد و HTML آن را میسازد).
ساخت Payload: سرور یک خروجی به نام RSC Payload (ساختار درختی React) میسازد. در این خروجی، <Cart> کامل ساخته شده، اما <Modal> فقط یک رفرنس به فایل جاوااسکریپت است.
ارسال به مرورگر (Client): مرورگر این درخت را دریافت میکند. HTML سبد خرید را فوراً نمایش میدهد. سپس جاوااسکریپت <Modal> را دانلود و اجرا میکند (عملیات Hydration).
نتیجه نهایی: حالا کاربر یک مودال دارد که باز و بسته میشود، و محتوای داخلش (سبد خرید) از قبل روی سرور ساخته و آماده شده است و نیازی به لودینگ مجدد دیتا سمت کلاینت ندارد.
مهم:
اجازه بدهید این تناقض ظاهری را برطرف کنیم. کلید حل این معما، درک تفاوت بین Server Components و SSR (Server-Side Rendering) است.
وقتی میگوییم "use client" یک مرز (Boundary) است، منظورمان این نیست که سرور تولید HTML آن را متوقف میکند.
اتفاقی که میافتد این است:
۱. تفاوت Client Component با کامپوننتهای سنتی (SPA): در یک برنامه خالص React (مثل Create React App)، کلاینت کامپوننتها واقعاً روی سرور رندر نمیشوند و مرورگر یک تگ خالی <div id="root"> میگیرد. اما Next.js اینطور کار نمیکند.
۲. SSR برای همه است: در Next.js، در بارگذاری اولیه صفحه، سرور هم Server Componentها و هم Client Componentها را یک بار اجرا میکند تا HTML اولیه (Pre-rendered HTML) را بسازد. چرا؟ برای اینکه کاربر صفحه سفید نبیند و سئو (SEO) عالی باشد.
۳. پس مرز (Boundary) یعنی چه؟
وقتی Next.js به "use client" میرسد میگوید: “این کامپوننت و بچههایش به جاوااسکریپت در مرورگر نیاز دارند (چون useState، useEffect یا onClick دارند). پس من علاوه بر اینکه HTML اولیه آنها را میسازم، کدهای جاوااسکریپت (JS Bundle) آنها را هم بستهبندی میکنم و به مرورگر میفرستم تا بعداً فعال (Hydrate) شوند.”
در مقابل، برای Server Componentها هیچ جاوااسکریپتی به مرورگر نمیفرستد.
در مورد مثال <Modal> و <Cart>:
سرور وقتی در حال ساخت HTML است، کد <Modal> (که Client است) را میبیند. وضعیت اولیهاش را میخواند (مثلاً isOpen: false). بر اساس این وضعیت، HTML آن را تولید میکند. چون مودال بسته است، محتوای <Cart> (که یک Server Component است و از قبل روی سرور پردازش شده و به عنوان children به مودال پاس داده شده) در HTML نهایی طوری قرار میگیرد که دیده نشود (یا اصلاً در DOM قرار نمیگیرد، بسته به کد شما).
خلاصه:
سرور بیخیالِ کامپوننتهای کلاینت نمیشود. سرور HTML آنها را میسازد، اما منطق تعاملی آنها (مثل کلیک کردن) را به عنوان یک وظیفه برای مرورگر (کلاینت) کنار میگذارد. به همین دلیل هیچ پرشی (Flicker) اتفاق نمیافتد.
عنوان کارت
پاسخ