React به عنوان یکی از قدرتمندترین کتابخانههای جاوااسکریپت برای توسعه واسط کاربری (UI) شناخته میشود. اما بهطور معمول، پردازش UI در سمت کلاینت انجام میشود و ارتباط با سرور از راه دور نیاز به تبادل دادههای بین دو محیط دارد. در این مورد، مفهوم React Server Components (RSCها) بهعنوان یک ادامه از اصول React مطرح میشود که به ایجاد ارتباط کلاینت-سرور از راه دور میپردازد و تجربهای نوین از توسعه UI ارائه میدهد.
چرا نیاز به کامپوننت های سروری داریم؟
نگاهی به دنیا قبل از ریاکت بیندازید. با زبانهای مانند PHP، ما یک ارتباط تنگتر بین کلاینت و سرور داشتیم. در معماری یکپارچه، میتوانستید به سرور مراجعه کنید تا دادهها را در همان صفحهای که در حال ساخت آن بودید، فراخوانی کنید. با این حال، نقاط ضعفی نیز وجود داشت - به ویژه مشکلات مرتبط با مقیاسپذیری برنامههای یکپارچه به دلیل وابستگیهای تیمی متقابل و تقاضاهای ترافیک بالا.
ریاکت برای ترکیبپذیری و انتقال تدریجی به پایگاههای کد موجود ایجاد شد. با پاسخ به دنیایی که اشتیاق زیادی برای تعاملات پویا داشت، مسائل کلاینت و سرور را از هم جدا کرد و باعث شد که بخش مقدمهای بسیار قابل انعطافتر برای ترکیب شود. این مسئله به ویژه برای تیمها بسیار حیاتی بود: دو کامپوننت ریاکت که توسط توسعهدهندگان مجزا ایجاد شدهاند، به خاطر اینکه در یک چارچوب یکسان عمل میکنند، به راحتی با یکدیگر همکاری میکردند.
برای دستیابی به این هدف، ریاکت باید بر روی استانداردهای وب موجود نوآوری کند. در طول دهه گذشته، از بین برنامههای چندصفحهای (MPA) و برنامههای تکصفحی (SPA)، بین تجزیه و تحلیل سمت کلاینت و سمت سرور، هدف همچنان همان بوده است: ارائه دادههای سریع، ارائه تعاملات پویا و حفظ تجربه برنامهنویسان عالی.
چگونه رندرینگ سمت سرور و تعلیق React مشکلاتی را حل کرد؟
در طول مسیری که به سمت کامپوننت های سرور میرسیم، مسائل دیگری نیز وجود داشته که حل کردن آنها بسیار حیاتی بوده است. برای درک بهتر نیاز به کامپوننت های سروری (RSC)، ابتدا نیاز به درک رندرینگ سمت سرور (SSR) و تعلیق (Suspense) مفید است.
SSR بر روی بارگذاری اولیه صفحه تمرکز دارد و HTML پیشرندرشده را به کلاینت ارسال میکند که باید پس از آن با جاوااسکریپت دریافتشده قبل از رفتار به عنوان یک برنامه React معمولی کار کند. همچنین SSR تنها یک بار اتفاق میافتد: هنگام مستقیم مراجعه به یک صفحه.
با SSR تنها، کاربر به سرعت HTML را دریافت میکند، اما باید قبل از تعامل با جاوااسکریپت منتظر "همه یا هیچ" باشد تا پیشرفت سریع قبل از تعامل اجرا شود:
تمام دادهها باید از سرور بازیابی شوند قبل از اینکه هر کدام از آنها نمایش داده شود.
تمام جاوااسکریپت باید از سرور بارگیری شود تا کلاینت بتواند با آن تغذیه(hydrated) شود.
تمام تغذیه باید در کلاینت تکمیل شود تا قبل از تعامل با هر چیزی امکان پذیر باشد.
برای حل این مشکلات، React تعلیق(Suspense) را ایجاد کرد که امکان پخش HTML از سمت سرور و تغذیه انتخابی در کلاینت را ممکن میسازد. با قرار دادن یک کامپوننت درون <Suspense>
، میتوانید به سرور بگویید که رندرینگ و تغذیه این کامپوننت را به اولویت پایین قرار دهد و به کامپوننتهای دیگر اجازه دهد بدون مسدود شدن توسط کامپوننتهای سنگینتر بارگیری شوند.
هنگامی که چندین کامپوننت در <Suspense>
دارید، React به ترتیبی که نوشتهاید درخت را پایین میآورد و به شما امکان پخش بهینه برنامه را میدهد. با این حال، اگر کاربر سعی کند با یک کامپوننت خاص تعامل کند، آن کامپوننت بر سایر کامپوننت اولویت دارد.
این به طور چشمگیری وضعیت را بهبود میبخشد، اما هنوز چندین مشکل باقیمانده وجود دارد:
باید تمام دادههای صفحه از سرور بازیابی شوند قبل از اینکه هر کامپوننتی نمایش داده شود. تنها راه حل این مشکل، بازیابی داده از سمت کلاینت در یک هوک ()useEffect
است که دورهٔ زمانی بلندتری نسبت به بازیابیهای سمت سرور دارد و تنها پس از رندرینگ و تغذیه کامپوننت انجام میشود.
تمام جاوااسکریپت صفحه در نهایت بارگیری میشود، حتی اگر به صورت ناهمزمان به مرورگر پخش شود. با افزایش پیچیدگی برنامه، مقدار کدی که کاربر بارگیری میکند نیز افزایش مییابد.
علاوه بر بهینهسازی هیدراتاسیون، تا زمانی که جاوااسکریپت سمت کلاینت بارگیری و برای آن کامپوننت اجرا شود، کاربران همچنان نمیتوانند با کامپوننتها تعامل داشته باشند.
بیشترین بار محاسباتی جاوااسکریپت همچنان بر کلاینت است که ممکن است بر روی هر نوع دستگاهی اجرا شود. چرا این کار را به سمت سرور قدرتمند و قابل پیشبینی منتقل نکنیم؟
همانطور که استانداردهای وب به جایی که چارچوبهای جاوااسکریپت مرزها را گسترش دادهاند،به پیشرفت دیگری نیاز است. روش بهتری برای ساخت برنامههای سریعتر وجود دارد.
توابع کامپوننتی سرور React چه کار میکنند؟
برای حل مشکلات فوق، React توابع کامپوننتی سرور(RCS) را ایجاد کرده است. توابع کامپوننتی سرور به صورت جداگانه دادهها را دریافت کرده و به طور کامل در سمت سرور رندر میشوند، و سپس HTML نتیجه به درخت کامپوننتی React سمت کلاینت جریان داده میشود و با سایر کامپوننتی سروری و کلاینتی به میزان لازم تداخل میکند.
این فرآیند نیاز به بازرنگی سمت کلاینت را از بین میبرد، بدین ترتیب عملکرد را بهبود میبخشد. برای هر کامپوننت کلاینت، هیدراتاسیون میتواند به همراه جریان توابع کامپوننتی سروری انجام شود، زیرا بار محاسباتی بین سمت کلاینت و سرور به اشتراک گذاشته میشود.
به عبارت دیگر، سرور که قدرتمندتر و فیزیکی تر از منابع دادههای شما است، به رندرینگ محاسباتی پرسشی پاسخ میدهد و تنها قسمتهای تعاملی کد را به کلاینت ارسال میکند.
وقتی یک تابع کامپوننتی سروری باید مجدداً رندر شود، به دلیل تغییر وضعیت، آن به طور سروری تازه میشود و بدون رفرش سخت به DOM موجود ترکیب میشود. در نتیجه، وضعیت کلاینت حتی در حالی که بخشهایی از نمایش از سمت سرور به روز شوند، حفظ میشود.
RCSs: عملکرد و اندازه بسته
توابع کامپوننتی سرور میتوانند به کاهش اندازه بسته جاوااسکریپت سمت کلاینت و بهبود عملکرد بارگذاری کمک کنند.
سنتیترین روش، در هنگام ناوبری در برنامه، کلاینت تمام کد و وابستگیهای داده را دانلود و سپس اجرا میکند. بدون یک چارچوب React دارای جداکردن کد، این به معنای ارسال کدهای اضافی به کاربران برای صفحهای که در آن هستند، است.
با این حال، توابع کامپوننتی سرور تمام وابستگیها را در سرور حل میکنند، به نزدیکی منابع داده برنامهی شما. همچنین، تنها کد را در سمت سرور رندر میکنند، که در انجام این کار سریعتر از ماشینهای کلاینت (مانند تلفنهای همراه) عمل میکند. سپس React فقط نتایج پردازش شده این همراه با کامپوننتی کلاینت را به مرورگر ارسال میکند.
به عبارت دیگر، با توابع کامپوننتی سرور، بارگذاری صفحه اولیه سریعتر و سبکتر است. زمان اجرای سمت کلاینت قابل کش شدن و قابل پیشبینی در اندازه است، و با افزایش برنامهی شما افزایش نمییابد. جاوااسکریپت کاربر-محور اضافی عمدتاً زمانی اضافه میشود که برنامهی شما نیاز به تعامل بیشتر سمت کلاینت دارد.
RCSs: تداخل و ادغام با Suspense
توابع کامپوننتی سرور به طور کامل با کد سمت کلاینت تداخل دارند، به این معنی که کامپوننت کلاینت و کامپوننت سرور میتوانند در یک درخت React هم زمان رندر شوند. با جابهجایی اکثر کد برنامه به سمت سرور، توابع کامپوننتی سرور به جلوگیری از fetch دادههای سمت کلاینت کمک میکنند و به سرعت وابستگیهای دادهای سمت سرور را حل میکنند.
در رندرینگ سمت کلاینت سنتی، کامپوننت ها از Suspense تا اتمام کار ناهمزمانی استفاده میکنند (و وضعیت پشتیبانی را نشان میدهند) در حالی که منتظر انجام کار ناهمزمانی هستند. با توابع کامپوننتی سرور، هم دریافت داده و هم رندرینگ در سرور انجام میشود، بنابراین انتظار در سمت سرور مدیریت میشود، که به کوتاه شدن دوره کلی برای افزایش سرعت رندرینگ وضعیت پشتیبانی و صفحه کامل کمک میکند.
مهم است بهیاد داشت که کامپوننت کلاینت هنوز در بارگذاری اولیه SSR میشوند. مدل توابع کامپوننتی سرور SSR یا Suspense را جایگزین نمیکند، بلکه به همراه آنها کار میکند تا تمام بخشهای برنامهی شما به کاربران شما طبق نیاز ارائه شوند.
محدودیتهای کامپوننت های سروری (RSCs):
تمامی کد نوشته شده برای کامپوننت های سروری باید قابل سریالسازی باشد، به این معنا که نمیتوانید از هوکهای چرخه زندگی مانند ()useEffect
یا state استفاده کنید.
با این حال، هنوز میتوانید از جانب کلاینت با استفاده از عملیات سرور از سرور تعامل داشته باشید، که به آنها در یک لحظه نزدیک خواهیم شد.
همچنین، کامپوننت های سروری از بهروزرسانیهای مداوم مثلا از طریق وبسوکتها پشتیبانی نمیکنند. در این موارد، استفاده از رویکرد دریافت یا پرسوجوی جانب کلاینت ضروری خواهد بود.
چگونه از کامپوننت های سرور React استفاده کنیم
زیبایی کامپوننت های سرور React در این است که واقعاً نیازی به دانش کامل از نحوه کار با آنها ندارید تا از آنها بهرهبرداری کنید. در روتر برنامه (App Router) که در Next.js 13.4 معرفی شده است و به جامعترین پیادهسازی کامپوننت های سرور React میپردازد، تمام کامپوننت به طور پیشفرض کامپوننت های سروری هستند.
اگر میخواهید از رویدادهای لایف سیکل مانند ()useEffect
یا وضعیت (state) استفاده کنید، نیاز به ترکیب کامپوننت های کلاینت (Client Component) دارید. وارد شدن به کامپوننت های کلاینت مسئلهای است که با نوشتن "use client" در بالای کامپوننت انجام میشود، اما برای راهنماییهای پیشرفتهتر، توصیه میشود که از مستندات استفاده کنید.
توازن کامپوننت های سروری و کلاینت
مهم است به یاد داشته باشید که کامپوننت های سرور React به منظور جایگزینی کامپوننت های کلاینت ایجاد نشدهاند. یک برنامه سالم از هر دو کامپوننت های سروری برای بازیابی دادههای پویا و کامپوننت های کلاینت برای تعاملات پویا استفاده میکند. چالش در تعیین زمان استفاده از هر کامپوننت قرار دارد.
به عنوان یک توسعهدهنده، در نظر داشته باشید که از کامپوننت های سروری برای بازیابی سمت سرور و دادهها استفاده کنید، در حالی که برای ویژگیها و تجربیات کاربری تعاملی از کامپوننت های کلاینت بهرهبرداری کنید. با یافتن تعادل مناسب، میتوانید یک برنامه با عملکرد بالا، کارآمد و جذاب ایجاد کنید.
مهمترین امر این است که ادامه دهید تا برنامههای خود را در محیطهای غیراستاندارد تست کنید: کامپیوترهای کمسرعت، تلفنهای همراه کمسرعت و اتصالات وایفای ضعیف را شبیهسازی کنید، و شاید شگفتزده شوید که با ترکیب مناسب کامپوننت چقدر بهتر عمل میکند.
کامپوننت های سرور React جواب کاملی برای مشکل بارگذاری بیش از حد اسکریپتهای سمت کلاینت نیستند، اما قطعاً به ما این امکان را میدهند که در کجا وقتی را که نیاز است، بار محاسبات را بر روی دستگاه کاربر قرار دهیم.
بازیابی بهبود یافته داده با Next.js
کامپوننت سرور React دادهها را در سرور بازیابی میکنند که نه تنها دسترسی امن به دادههای پشتیبان را فراهم میکند، بلکه با کاهش تعامل سرور-کلاینت، عملکرد را بهبود میبخشد. همراه با اصلاحات Next.js، کامپوننت سرور React همچنین امکان حافظهپنداری هوشمندانه از داده، چندین بازیابی در یک سفر تکراری، و ادغام خودکار درخواستهای ()fetch
را فراهم میکنند - که همه باعث افزایش کارآیی ارسال داده به سمت کلاینت میشود.
شاید مهمترین امر این باشد که بازیابی داده در سمت سرور به کاهش نوسانات بازیابی داده سمت کلاینت کمک میکند، جایی که درخواستها به یکدیگر انباشته شده و باید به ترتیب تجزیه و تحلیل شوند تا کاربر بتواند ادامه دهد. بازیابیهای سمت سرور دارای هزینه کمتری هستند، زیرا کل کلاینت را مسدود نمیکنند و به سرعت بیشتری تجزیه و تحلیل میشوند.
علاوه بر این، دیگر نیازی به متدهای مخصوص Next.js مانند ()getServerSideProps
و ()getStaticProps
نیست، که کنترل دقیق کافی برای کامپوننت انفرادی را ارائه نمیکنند و معمولاً دادهها را بیش از حد بازیابی میکنند. (وقتی کاربر به صفحه منتقل میشود، همه دادهها بازیابی میشدند، بدون در نظر گرفتن کامپوننت که به واقعیت با آنها تعامل میکردند.
در روتر برنامه Next.js، تمام دادههای بازیابی شده به طور پیشفرض به صورت استاتیک، در زمان ساخت نمایش داده میشوند. با این حال، این امر به سادگی قابل تغییر است: Next.js گزینههای بازیابی داده را گسترش میدهد تا انعطاف پذیری در حافظهپنداری و قوانین بازرسی مجدد را فراهم کند.
میتوانید از گزینه {next: {revalidate: number}}
برای تازهسازی دادههای استاتیک در فواصل زمانی مشخص یا هنگام تغییرات پشتیبان (بازسازی استاتیک تدریجی) استفاده کنید، در حالی که گزینه {cache: 'no-store'}
میتواند در درخواست بازیابی برای دادههای پویا (رندرینگ سمت سرور) منتقل شود.
همه این امور کامپوننت های سرور React در روتر برنامه Next.js را ابزاری قدرتمند برای بازیابی دادههای کارآمد، امن و پویا میکنند که به طور پیشفرض توسط حافظهپنداری به ارمغان میآید تا تجربهی کاربری با عملکرد بالا ارائه دهد.
عملیاتهای سرور: گامهای ابتدایی React در دامنهٔ تغییرپذیری
در سیاق RSCها، عملیاتهای سرور توابعی هستند که شما در یک RSC در سمت سرور تعریف میکنید و سپس میتوانید آنها را از طریق مرز سرور/کلاینت منتقل کنید. وقتی کاربر در سمت کلاینت با برنامهٔ شما تعامل دارد، میتوانند بهطور مستقیم از عملیاتهای سرور استفاده کرده و این عملیاتها بهطور ایمن در سمت سرور اجرا میشوند.
این رویکرد یک تجربهٔ تماس فراخوانی روی Remote Procedure Call (RPC) بین کلاینت و سرور را فراهم میکند. بهجای نوشتن یک مسیر جداگانه برای ارتباط با سرور، شما میتوانید بهصورت مستقیم از عملیاتهای سرور در کامپوننتی کلاینت خود فراخوانی کنید.
همچنین بهخاطر داشته باشید که روتر برنامهٔ Next.js بهطور کامل بر پایهٔ حافظهٔ پنهان دادههای هوشمند، بازبینی و تغییر مبنا است. عملیاتهای سرور در Next.js به شما اجازه میدهد هم به حافظهٔ پنهان تغییر دهید و هم درخت React را در همان درخواست رفت و برگشت به سمت سرور بهروز کنید - همه در حالی که از طریق ناوبری سرور، سالمی حافظهٔ پنهان کلاینت را حفظ میکنید.
بهطور خاص، عملیاتهای سرور برای انجام وظایفی مانند بهروزرسانی پایگاهداده یا ارسال فرم طراحی شدهاند. به عنوان مثال، آنها میتوانند قابلیت ارتقاء تدریجی فرمهای شما را افزایش دهند، به این معنا که حتی اگر جاوااسکریپت هنوز بارگیری نشده باشد، کاربر همچنان میتواند با فرم تعامل داشته باشد و عملیاتهای سرور ارسال و پردازش دادههای فرم را انجام خواهند داد.
فرصتهایی که عملیاتهای سرور ارائه میدهند، همچنین برای افزایش تدریجی و کاهش کار توسعه در مورد رابطهای برنامهنویسی کاربردی، به منظور دسترسی، استفادهپذیری و تجربهٔ توسعهدهندگان بسیار مناسب هستند.
بگذارید Next.js کارهای سنگین را انجام دهد
Next.js اولین چارچوب است که تمام معماری React برای کامپوننتی سرور، عملیاتهای سرور، تعلیق، انتقالها و همه چیز دیگری را که با انتشار RSCها تغییر کرده است، یکپارچه میکند.
وقتی شما بر روی ساخت محصول خود تمرکز میکنید، Next.js از استریمینگ استراتژیک و حافظهٔ پنهان هوشمند استفاده میکند تا اطمینان حاصل کند که پردازش برنامهٔ شما متوقف نمیشود و دادههای پویا را با سرعت بالا ارائه میدهد.
Next.js تلاش میکند تا با حفظ پایداری، قابلیت اطمینان و سازگاری با نسخههای قبلی، در لبه تازههای React باقی بماند. این چارچوب بهطور ادامهای تنظیمات پیشفرض هوشمند را برای تیم شما ارائه میدهد تا بهسرعت ایجاد نسخههای تکراری را داشته باشید، در حالی که انعطافپذیری و مقیاسپذیری برای پروژههای هر دامنه را حفظ میکند.
از کجا باید به جلو بروید؟
خلاصهاش این است: کامپوننتی سرور React راهی بومی برای تعامل با سرور را مستقیماً درون کامپوننتی ارائه میدهند، که هم کد و هم بار شناختی تعامل با دادههای پویا را کمرنگتر میکند. کامپوننت کلاینت همچنان بهطور کامل کاربردی و قابل استفاده هستند، مانند گذشته. وظیفهٔ جدید شما انتخاب زمان استفاده از هر یک از آنها است.
برای راهنمایی بیشتر در مورد این موضوع، به مستندات Next.js سر بزنید.
اگر به اطلاعات زیادی نیاز دارید می توانید در زیر کامنت بزارید.