أساسيات React.js

مفاهيم العرض من جانب الخادم

18 دقيقة الدرس 36 من 40

فهم العرض من جانب الخادم (SSR)

العرض من جانب الخادم هو تقنية حيث يتم عرض مكونات React على الخادم وإرسالها كـ HTML إلى العميل، بدلاً من عرض كل شيء في المتصفح. هذا النهج له تأثيرات كبيرة على الأداء وتحسين محركات البحث وتجربة المستخدم.

SSR مقابل CSR مقابل SSG

هناك ثلاث استراتيجيات رئيسية لعرض تطبيقات React:

العرض من جانب العميل (CSR):
  • يتم إرسال حزمة JavaScript إلى المتصفح
  • React يعرض المكونات في المتصفح
  • HTML الأولي بسيط جداً (عادة فقط div)
  • المحتوى مرئي بعد تنفيذ JS
  • الأفضل لـ: التطبيقات التفاعلية مع مستخدمين مصادق عليهم
العرض من جانب الخادم (SSR):
  • React يعرض المكونات على الخادم لكل طلب
  • يتم إرسال HTML كامل إلى المتصفح
  • المحتوى مرئي فوراً
  • ثم يقوم JavaScript بـ "الترطيب" للصفحة
  • الأفضل لـ: المحتوى الديناميكي، الصفحات المخصصة، البيانات الفورية
توليد الموقع الثابت (SSG):
  • React يعرض المكونات في وقت البناء
  • يتم توليد ملفات HTML مُعدة مسبقاً
  • يتم تقديمها كملفات ثابتة (متوافقة مع CDN)
  • أوقات تحميل سريعة للغاية
  • الأفضل لـ: المدونات، الوثائق، صفحات التسويق

مثال أساسي لـ SSR مع Express

// server.js import express from 'express'; import React from 'react'; import { renderToString } from 'react-dom/server'; import App from './App'; const app = express(); app.get('*', (req, res) => { const appHtml = renderToString(<App />); const html = ` <!DOCTYPE html> <html lang="ar" dir="rtl"> <head> <meta charset="UTF-8"> <title>تطبيق React مع SSR</title> </head> <body> <div id="root">${appHtml}</div> <script src="/bundle.js"></script> </body> </html> `; res.send(html); }); app.listen(3000, () => { console.log('خادم SSR يعمل على المنفذ 3000'); });

فهم الترطيب (Hydration)

الترطيب هو العملية التي يقوم فيها React بـ "الارتباط" بـ HTML المعروض من الخادم، مما يجعله تفاعلياً:

// نقطة الدخول من جانب العميل import React from 'react'; import { hydrateRoot } from 'react-dom/client'; import App from './App'; // استخدم hydrateRoot بدلاً من createRoot لـ SSR const container = document.getElementById('root'); hydrateRoot(container, <App />);
عدم تطابق الترطيب: إذا لم يتطابق HTML المعروض من الخادم مع ما يتوقعه React على العميل، ستحصل على أخطاء ترطيب. الأسباب الشائعة تشمل:
  • استخدام APIs خاصة بالمتصفح أثناء العرض من الخادم
  • اختلافات التاريخ/الوقت بين الخادم والعميل
  • القيم العشوائية أو IDs المولدة أثناء العرض
  • سكريبتات خارجية تعدل DOM

فوائد SEO من SSR

العرض من جانب الخادم يوفر مزايا كبيرة لتحسين محركات البحث:

// مسار خادم مع علامات meta ديناميكية app.get('/product/:id', async (req, res) => { const product = await fetchProduct(req.params.id); const appHtml = renderToString( <App initialData={{ product }} /> ); const html = ` <!DOCTYPE html> <html lang="ar" dir="rtl"> <head> <title>${product.name} - متجرنا</title> <meta name="description" content="${product.description}"> <meta property="og:title" content="${product.name}"> <meta property="og:image" content="${product.image}"> </head> <body> <div id="root">${appHtml}</div> <script> window.__INITIAL_DATA__ = ${JSON.stringify({ product })}; </script> <script src="/bundle.js"></script> </body> </html> `; res.send(html); });
فوائد SEO:
  • فهرسة فورية: محركات البحث ترى المحتوى الكامل فوراً
  • علامات Meta ديناميكية: كل صفحة يمكن أن يكون لها عناوين وأوصاف وعلامات Open Graph مخصصة
  • المشاركة الاجتماعية: المعاينات الغنية تعمل بشكل مثالي على Facebook وTwitter وLinkedIn
  • تصنيفات أفضل: Core Web Vitals تتحسن مع رسم المحتوى الأولي الأسرع

التأثيرات على الأداء

SSR يؤثر على الأداء بطرق مختلفة:

الوقت حتى أول بايت (TTFB): SSR يزيد TTFB لأن الخادم يحتاج إلى:
  • جلب البيانات من قواعد البيانات/APIs
  • عرض مكونات React إلى HTML
  • إرسال HTML الكامل
المقايضة: TTFB أبطأ، لكن First Contentful Paint (FCP) أسرع
الوقت حتى التفاعل (TTI): مع SSR:
  • المحتوى مرئي فوراً (HTML)
  • لكن ليس تفاعلياً حتى يتم تحميل JavaScript وترطيبه
  • الفجوة بين FCP وTTI يمكن أن تربك المستخدمين
الحل: التحسين التدريجي، حالات التحميل، SSR المتدفق

مكونات خادم React (RSC)

مكونات خادم React هي نموذج جديد يجمع بين SSR وميزات React الحديثة:

// UserProfile.server.jsx - مكون خادم import { db } from './database'; export default async function UserProfile({ userId }) { // هذا يعمل فقط على الخادم const user = await db.users.find(userId); const posts = await db.posts.findByUser(userId); return ( <div> <h1>{user.name}</h1> <p>{user.bio}</p> {/* مكون عميل للتفاعلية */} <PostList posts={posts} /> </div> ); } // PostList.client.jsx - مكون عميل 'use client'; import { useState } from 'react'; export default function PostList({ posts }) { const [filter, setFilter] = useState('all'); return ( <div> <button onClick={() => setFilter('all')}>الكل</button> <button onClick={() => setFilter('popular')}>شائع</button> {posts .filter(post => filter === 'all' || post.popular) .map(post => <Post key={post.id} {...post} />)} </div> ); }
فوائد مكونات الخادم:
  • حجم حزمة صفر: مكونات الخادم لا ترسل JavaScript إلى العميل
  • وصول مباشر للخلفية: الوصول إلى قواعد البيانات، أنظمة الملفات، APIs الداخلية مباشرة
  • تقسيم الكود التلقائي: فقط مكونات العميل يتم تجميعها
  • أداء محسّن: JavaScript أقل = ترطيب أسرع

SSR المتدفق

أطر SSR الحديثة تدعم التدفق، إرسال HTML في أجزاء:

import { renderToPipeableStream } from 'react-dom/server'; app.get('*', (req, res) => { const stream = renderToPipeableStream( <App />, { onShellReady() { // إرسال الغلاف الأولي فوراً res.setHeader('Content-Type', 'text/html'); stream.pipe(res); }, onError(error) { console.error(error); res.status(500).send('خطأ في الخادم'); } } ); });
فوائد التدفق:
  • إرسال غلاف الصفحة فوراً (الرأس، التنقل)
  • تدفق المحتوى عندما تصبح البيانات متاحة
  • المستخدمون يرون المحتوى بشكل أسرع (العرض التدريجي)
  • أداء محسوس أفضل

متى تستخدم كل استراتيجية

// CSR - تطبيقات الصفحة الواحدة // جيد لـ: لوحات التحكم، التطبيقات المصادق عليها const config = { rendering: 'client', caching: 'none', seo: 'not-critical' }; // SSG - المحتوى الثابت // جيد لـ: المدونات، الوثائق، الصفحات المقصودة const config = { rendering: 'build-time', caching: 'aggressive', seo: 'critical', revalidate: 3600 // إعادة البناء كل ساعة }; // SSR - المحتوى الديناميكي // جيد لـ: التجارة الإلكترونية، الأخبار، التدفقات المخصصة const config = { rendering: 'per-request', caching: 'strategic', seo: 'critical', personalization: true }; // مختلط - مزج الاستراتيجيات // جيد لـ: معظم تطبيقات الإنتاج const config = { rendering: 'mixed', routes: { '/blog/*': 'ssg', '/dashboard/*': 'csr', '/products/*': 'ssr' } };
تمرين 1: قم بتحليل تطبيق React الحالي الخاص بك. لكل مسار رئيسي، حدد استراتيجية العرض المثلى (CSR أو SSR أو SSG) وبرر اختيارك بناءً على:
  • تكرار تحديث المحتوى
  • متطلبات SEO
  • احتياجات المصادقة
  • أهداف الأداء
تمرين 2: أنشئ خادم SSR بسيط باستخدام Express:
  • قم بإعداد Express مع عرض React SSR
  • أنشئ مساراً يجلب البيانات ويعرض مكوناً
  • مرر البيانات الأولية إلى العميل للترطيب
  • تعامل مع الترطيب في حزمة العميل
تمرين 3: قارن مقاييس الأداء:
  • ابنِ صفحة بسيطة مع CSR (create-react-app)
  • ابنِ نفس الصفحة مع SSR (Next.js أو مخصص)
  • قس TTFB وFCP وLCP وTTI لكلاهما
  • حلل أيهما يؤدي بشكل أفضل في سيناريوهات مختلفة (شبكات سريعة/بطيئة)