أوضاع التصيير SSR و CSR
اكتشف Charlie أن جميع صفحات MegaShop تستخدم SSR، مما يضع ضغطاً هائلاً على الخوادم—حيث يتم تصيير ملايين صفحات المنتجات مع كل طلب. محتوى الصفحة الرئيسية نادراً ما يتغير، لكنه يُعاد تصييره مع كل طلب. لوحة التحكم لا تحتاج SEO، لكنها تُصيَّر على الخادم. التصيير الهجين يتيح لكل صفحة اختيار الاستراتيجية المثلى: SSG للصفحة الرئيسية، ISR لقوائم المنتجات، SSR لصفحات التفاصيل، و CSR لمركز المستخدم.
1. ما ستتعلمه
- تكوين أوضاع التصيير routeRules: ssr/csr/swr/prerender/hydrate
- ISR (إعادة التصيير الثابت التزايدي): ذاكرة تخزين SWR المؤقتة وإعادة التحقق عند الطلب
- تصيير حافة ESR: حوسبة حافة CDN في Nitro
- دليل عملي للتصيير الهجين: اختيار استراتيجيات مختلفة لصفحات مختلفة
- MegaShop: تصميم تخزين ISR المؤقت لصفحات المنتجات بعدد مليون صفحة
2. قصة حقيقية لمهندس معمارية
(1) نقطة الألم: SSR على مستوى الموقع يُغرق الخادم
يتعامل MegaShop مع 5000 طلب في الثانية، جميعها عبر SSR. محتوى الصفحة الرئيسية يتغير مرة واحدة فقط يومياً، لكنه يُصيَّر مع كل طلب. قائمة المنتجات تُحدَّث كل ساعة، مما يؤدي إلى التصيير المتكرر لمليون صفحة تفاصيل منتج. استخدام وحدة المعالجة المركزية للخادم وصل إلى 90%، لذا يخطط Bob لإضافة 10 خوادم أخرى.
(2) حل التصيير الهجين
routeRules في Nuxt 3 يتيح لكل مسار امتلاك استراتيجية تصيير خاصة:
// nuxt.config.ts
routeRules: {
'/': { prerender: true }, // الصفحة الرئيسية: بناء مرة واحدة
'/products': { swr: 3600 }, // قائمة المنتجات: تخزين مؤقت لساعة
'/products/**': { swr: 86400 }, // تفاصيل المنتج: تخزين مؤقت ليوم
'/admin/**': { ssr: false } // لوحة التحكم: العميل فقط
}
(3) الفوائد: انخفاض حمولة الخادم بنسبة 85%
بعد تطبيق التصيير الهجين، انخفضت طلبات SSR من 5000 في الثانية إلى 750 في الثانية، وانخفض عدد الخوادم من 10 إلى 3، وانخفضت التكلفة بنسبة 70%.
3. وضع تصيير routeRules
(1) شجرة قرار التصيير الهجين
flowchart TB
A[طلب الصفحة] --> B{تحتاج SEO؟}
B -->|لا| C[CSR - ssr: false]
B -->|نعم| D{المحتوى يتغير؟}
D -->|أبداً| E[SSG - prerender: true]
D -->|نادراً| F[ISR - swr: ثوانٍ]
D -->|بشكل متكرر| G[SSR - الوضع الافتراضي]
D -->|يختلف حسب المنطقة| H[ESR - تصيير الحافة]
C --> I[لوحة التحكم / لوحة المعلومات / مركز المستخدم]
E --> J[حول / قانوني / صفحة هبوط]
F --> K[قائمة المنتجات / صفحات الفئات]
G --> L[تفاصيل المنتج / نتائج البحث]
H --> M[الصفحة الرئيسية العالمية / المحتوى الإقليمي]
(2) مرجع سريع لخيار routeRules
| الخيار | القيمة | التأثير | السيناريوهات المناسبة |
|---|---|---|---|
| ssr | boolean | مفتاح SSR | false = CSR |
| csr | boolean | مفتاح CSR | خيار قديم |
| swr | number/string | مدة تخزين ISR المؤقت (بالثواني) | صفحات تُحدَّث دورياً |
| prerender | boolean | تصيير مسبق أثناء البناء | صفحة ثابتة |
| hydrate | boolean | إرواء فقط، بدون تصيير | تحسين خاص |
| redirect | string | إعادة توجيه 301/302 | نقل URL |
| cors | boolean | نطاق مشترك | مسار API |
| headers | object | رؤوس استجابة مخصصة | تخزين مؤقت/أمان |
(1) ▶ مثال: تكوين routeRules كامل
// nuxt.config.ts
export default defineNuxtConfig({
routeRules: {
// الصفحة الرئيسية: تصيير مسبق أثناء البناء
'/': { prerender: true },
// صفحات ثابتة
'/about': { prerender: true },
'/privacy': { prerender: true },
// قائمة المنتجات: ISR مع تخزين مؤقت لساعة
'/products': { swr: 3600 },
// تفاصيل المنتج: ISR مع تخزين مؤقت لمدة 24 ساعة
'/products/**': { swr: 86400 },
// الفئات: ISR مع تخزين مؤقت لمدة 6 ساعات
'/categories/**': { swr: 21600 },
// لوحة التحكم: تصيير من جهة العميل فقط
'/admin/**': { ssr: false },
// صفحات المستخدم: من جهة العميل
'/profile/**': { ssr: false },
// API: رؤوس CORS
'/api/**': { cors: true },
// عمليات إعادة التوجيه
'/shop/': { redirect: '/products/', redirectCode: 301 },
// رؤوس مخصصة للأصول الثابتة
'/_nuxt/**': { headers: { 'cache-control': 'max-age=31536000' } }
}
})
الناتج:
// تم التنفيذ بنجاح
4. ISR إعادة التصيير الثابت التزايدي
(1) سير عمل ISR
sequenceDiagram
participant U as المستخدم
participant C as CDN/Cache
participant S as خادم Nuxt
participant D as قاعدة البيانات
U->>C: GET /products/123
C->>C: أصاب التخزين المؤقت؟ (ضمن فترة swr)
C-->>U: HTML مخزّن مؤقتاً (فوري)
Note over C,S: بعد انتهاء فترة swr
U->>C: GET /products/123
C->>C: التخزين المؤقت قديم
C-->>U: HTML قديم (لا يزال سريعاً)
C->>S: إعادة التحقق في الخلفية
S->>D: جلب أحدث البيانات
D-->>S: بيانات محدّثة
S-->>C: HTML جديد
Note over C: الطلب التالي يحصل على HTML جديد
(2) مقارنة بين ISR و SSR و SSG
| البُعد | SSR | SSG (prerender) | ISR (swr) |
|---|---|---|---|
| توقيت التصيير | مع كل طلب | أثناء البناء | تحديث في الخلفية بعد انتهاء التخزين المؤقت |
| وقت الاستجابة | 🟡 متوسط | 🟢 الأسرع | 🟢 سريع (إصابة التخزين المؤقت) |
| حداثة البيانات | ✅ الأحدث | ❌ ثابت وقت البناء | ⚠️ أقصى تأخير = ثوانٍ swr |
| حمولة الخادم | 🔴 عالية | 🟢 الأقل | 🟢 منخفضة |
| حالات الاستخدام | بيانات آنية | لا تتغير أبداً | تحديثات مجدولة |
(1) ▶ مثال: صفحة قائمة منتجات ISR
// nuxt.config.ts - تكوين ISR
export default defineNuxtConfig({
routeRules: {
// قائمة المنتجات: تقديم من التخزين المؤقت، إعادة التحقق كل 60 ثانية
'/products': { swr: 60 },
// تفاصيل المنتج: تخزين مؤقت لساعة
'/products/**': { swr: 3600 }
}
})
الناتج:
// تم التنفيذ بنجاح
(2) ▶ مثال: إبطال تخزين ISR المؤقت برمجياً
// server/api/invalidate-cache.post.ts
export default defineEventHandler(async (event) => {
const { path } = await readBody(event)
// مسح المسار المُخزَّن مؤقتاً المحدد
await useStorage('cache').removeItem(`nitro:routes:${path}`)
return { message: `Cache invalidated for ${path}`, timestamp: Date.now() }
})
الناتج:
// تم التنفيذ بنجاح
5. تصيير حافة ESR
(1) مبادئ تصيير الحافة
ESR (تصيير جانب الحافة) ينفذ التصيير في عُقد حافة CDN، ويسترجع المستخدمون HTML من أقرب عُقدة.
| البُعد | SSR المركزي | ESR |
|---|---|---|
| موقع الخادم | منطقة واحدة | عُقد حافة عالمية |
| زمن الاستجابة | يعتمد على المسافة | الحد الأدنى (وصول محلي) |
| حالات الاستخدام | مستخدمون في منطقة واحدة | مستخدمون عالميون |
| إعدادات Nitro المسبقة | node-server | cloudflare-pages / vercel-edge |
(1) ▶ مثال: تكوين نشر Vercel Edge
// nuxt.config.ts
export default defineNuxtConfig({
nitro: {
preset: 'vercel-edge'
},
routeRules: {
// الصفحة الرئيسية مصيَّرة عند الحافة للمستخدمين العالميين
'/': { swr: 3600 },
'/products/**': { swr: 86400 }
}
})
الناتج:
// تم التنفيذ بنجاح
6. استراتيجية التصيير الهجين لـ MegaShop
(1) تخطيط استراتيجية تصيير صفحات MegaShop
| الصفحة | URL | الاستراتيجية | SWR | السبب |
|---|---|---|---|---|
| الصفحة الرئيسية | / | SSG | - | محتوى مستقر، يُصيَّر أثناء البناء |
| صفحة حول | /about | SSG | - | تكاد لا تتغير |
| قائمة المنتجات | /products | ISR | 3600 | المنتجات تُحدَّث كل ساعة |
| صفحة الفئات | /categories/** | ISR | 21600 | تُحدَّث يومياً |
| تفاصيل المنتج | /products/** | ISR | 86400 | تُحدَّث يومياً |
| نتائج البحث | /search | SSR | - | استعلام آني |
| سلة التسوق | /cart | CSR | - | للأعضاء فقط |
| مركز المستخدم | /profile/** | CSR | - | SEO غير مطلوب |
| لوحة التحكم | /admin/** | CSR | - | SEO غير مطلوب |
| API | /api/** | - | - | API بيانات فقط |
(1) ▶ مثال: routeRules كامل لـ MegaShop
// nuxt.config.ts
export default defineNuxtConfig({
ssr: true,
routeRules: {
// SSG: تصيير مسبق أثناء البناء
'/': { prerender: true },
'/about': { prerender: true },
'/privacy': { prerender: true },
'/contact': { prerender: true },
// ISR: تخزين مؤقت مع إعادة التحقق
'/products': { swr: 3600 }, // ساعة واحدة
'/categories/**': { swr: 21600 }, // 6 ساعات
'/products/**': { swr: 86400 }, // 24 ساعة
// SSR: تصيير آني
'/search': { swr: 0 },
// CSR: من جهة العميل فقط
'/cart': { ssr: false },
'/checkout/**': { ssr: false },
'/profile/**': { ssr: false },
'/admin/**': { ssr: false },
// API
'/api/**': { cors: true },
// أصول ثابتة: تخزين مؤقت طويل
'/_nuxt/**': { headers: { 'cache-control': 'public, max-age=31536000, immutable' } },
'/images/**': { headers: { 'cache-control': 'public, max-age=86400' } }
}
})
الناتج:
// تم التنفيذ بنجاح
(2) حل انتهاء تخزين ISR المؤقت لصفحات المنتجات بعدد مليون صفحة
| الحدث | سياسة الانتهاء | التنفيذ |
|---|---|---|
| تحديث سعر المنتج | إبطال صفحة المنتج المتأثرة | useStorage('cache').removeItem() |
| منتجات جديدة | إبطال قائمة المنتجات | إبطال دفعي لتخزين /products المؤقت |
| تغييرات الفئات | إبطال صفحات الفئات | إبطال /categories/xxx |
| تخفيض على مستوى الموقع | مسح جميع صفحات المنتجات | مسح التخزين المؤقت بالكامل |
| تغييرات المخزون | إبطال المنتجات المتأثرة | تشغيل عبر Webhook |
7. مثال شامل: التحقق من أوضاع تصيير MegaShop
<!-- pages/admin/dashboard.vue - وضع CSR -->
<template>
<div>
<h1>Admin Dashboard</h1>
<p>Rendering mode: Client-Side Only</p>
<p>Current time: {{ currentTime }}</p>
</div>
</template>
<script setup lang="ts">
// CSR: هذه الصفحة غير مصيَّرة على الخادم
definePageMeta({ ssr: false, layout: 'sidebar' })
const currentTime = ref(new Date().toISOString())
onMounted(() => {
setInterval(() => {
currentTime.value = new Date().toISOString()
}, 1000)
})
</script>
<!-- pages/products/[id].vue - وضع ISR -->
<template>
<div v-if="product">
<h1>{{ product.name }}</h1>
<p>${{ product.price }} USD</p>
<p>Cache: revalidated every 24 hours</p>
</div>
</template>
<script setup lang="ts">
// ISR: مخزّن مؤقتاً لمدة 24 ساعة عبر routeRules swr: 86400
const route = useRoute()
const { data: product } = await useFetch(`/api/products/${route.params.id}`)
</script>
❓ أسئلة شائعة
📖 ملخص
- routeRules يُكوِّن استراتيجية تصيير منفصلة لكل مسار: SSR/CSR/SSG/ISR/ESR
- ISR (swr) يوفر التوازن الأمثل: إصابات التخزين المؤقت بنفس سرعة SSG، والتحديث التلقائي عند الانتهاء يضمن حداثة البيانات
- ESR يُصيِّر عند حافة CDN، مما يوفر أقل زمن استجابة للمستخدمين عالمياً
- استراتيجية MegaShop الهجينة: الصفحة الرئيسية (SSG) + المنتجات (ISR) + البحث (SSR) + الإدارة (CSR)
- لصفحات المنتجات بعدد مليون، يستخدم ISR التخزين المؤقت القائم على الزيارات مع الإبطال المبني على الأحداث؛ لا تُخزَّن جميع الصفحات مسبقاً
📝 تمارين
- تمرين أساسي (الصعوبة: ⭐): كوِّن routeRules لتمكين التصيير المسبق للصفحة الرئيسية و CSR لصفحة لوحة التحكم، ثم تحقق من الفروق في مخرجات "عرض المصدر" بين الوضعين.
- تمرين متقدم (الصعوبة: ⭐⭐): كوِّن ISR (swr: 60) لقائمة المنتجات، وبعد 60 ثانية، حدِّث الصفحة لترى ما إذا كانت البيانات قد تحدثت.
- تحدٍ (الصعوبة: ⭐⭐⭐): نفِّذ واجهة API لإبطال تخزين ISR المؤقت. عند تحديث بيانات المنتج، استدعِ /api/invalidate-cache لإبطال التخزين المؤقت المقابل، وتحقق أن الطلب التالي يحصل على البيانات الجديدة.
---|



