تحسين الأداء
أصبح MegaShop متاحاً الآن، لكن درجته في Lighthouse هي 45 فقط—يستغرق 4.5 ثانية لتحميل الشاشة الأولى، والصور غير محسّنة، وحزمة JS كبيرة جداً. يحتاج Charlie إلى درجة Lighthouse تتجاوز 90 وLCP أقل من ثانيتين للشاشة الأولى للبقاء تنافسياً.
1. ما ستتعلمه
- تقسيم الكود واهتزاز الشجرة: تحسين بناء Vite وتحليل الحزمة
- تحسين الصور: @nuxt/image + WebP + تحميل كسول + srcset متجاوب
- التحميل الكسول للمكوّنات والمعالجة غير المتزامنة: بادئة "Lazy" + Suspense
- استراتيجية التخزين المؤقت: عناوين تخزين HTTP المؤقت + Nitro KV + CDN
- تحسين MegaShop لدرجة Lighthouse 90+ في الممارسة العملية
2. قصة حقيقية لمهندس معماري
(1) نقطة الألم: درجة Lighthouse 45، 4.5 ثانية حتى أول رسم للمحتوى
فتحت Alice موقع MegaShop على هاتفها واستغرق ظهور المنتجات 4.5 ثانية. شغّل Charlie تدقيق Lighthouse ووجد أن حزمة JS بحجم 2.8 ميغابايت، وجميع الصور بصيغة PNG غير مضغوطة، ولا توجد عناوين تخزين مؤقت، وكود 50 مكوّناً يُحمّل على الشاشة الأولى.
(2) حلول تحسين الأداء في Nuxt 3
يتضمن Nuxt 3 مجموعة متنوعة من ميزات تحسين الأداء المدمجة:
TYPESCRIPT
// تحسين الصور باستخدام @nuxt/image
<NuxtImg src="/products/123.webp" width="400" height="400" loading="lazy" />
(3) الأداء: درجة Lighthouse 92، LCP 1.3 ثانية
بعد التحسين، انخفض حجم حزمة JS إلى 180 كيلوبايت (gzip)، والصور تُقدَّم تلقائياً بصيغة WebP ومتجاوبة، وLCP للشاشة الأولى 1.3 ثانية، ودرجة Lighthouse 92.
3. تقسيم الكود واهتزاز الشجرة
(1) شجرة قرار تحسين الأداء
flowchart TB
A[مشكلة أداء] --> B{ما النوع؟}
B -->|رسم أول بطيء| C[تقسيم الكود]
B -->|صور كبيرة| D[تحسين الصور]
B -->|تحميلات لاحقة بطيئة| E[استراتيجية التخزين المؤقت]
B -->|JS كبيرة جداً| F[اهتزاز الشجرة]
C --> C1[مكوّنات كسولة]
C --> C2[استيرادات ديناميكية]
D --> D1[وحدة @nuxt/image]
D --> D2[تحويل WebP]
E --> E1[عناوين تخزين HTTP المؤقت]
E --> E2[تخزين Nitro KV المؤقت]
F --> F1[فحص الحزمة بـ rollup-plugin-visualizer]
(2) تحليل الحزمة
BASH
npm install -D rollup-plugin-visualizer
TYPESCRIPT
// nuxt.config.ts
import { visualizer } from 'rollup-plugin-visualizer'
export default defineNuxtConfig({
vite: {
plugins: [
visualizer({ filename: 'bundle-stats.html', open: true })
],
build: {
rollupOptions: {
output: {
manualChunks: {
'vendor': ['vue', 'vue-router'],
'pinia': ['pinia']
}
}
}
}
}
})
(1) ▶ مثال: تقسيم كود المكوّن الكسول
VUE
<template>
<div>
<!-- الأساسي: يُحمّل فوراً -->
<AppHeader />
<ProductHero :product="featured" />
<!-- تحت الطي: تحميل كسول -->
<LazyProductGrid :products="all" />
<LazyProductReviews :product-id="id" />
<!-- شرطي: يُحمّل فقط عند ظهوره -->
<LazyNewsletterSignup v-if="showNewsletter" />
</div>
</template>
الناتج:
TEXT
// التنفيذ ناجح
(3) مقارنة قبل وبعد التحسين
| المقياس | قبل التحسين | بعد التحسين | التحسين |
|---|---|---|---|
| حزمة JS (gzip) | 2.8 ميغابايت | 180 كيلوبايت | -94٪ |
| عدد المكوّنات على الشاشة الأولى | 50 | 8 | -84٪ |
| تحميل JS على الشاشة الأولى | 2.8 ميغابايت | 85 كيلوبايت | -97٪ |
| LCP | 4.5 ثانية | 1.3 ثانية | -71٪ |
4. تحسين الصور
(1) ▶ مثال: تثبيت @nuxt/image
BASH
npm install @nuxt/image
الناتج:
TEXT
# تم تنفيذ الأمر بنجاح
TYPESCRIPT
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['@nuxt/image'],
image: {
quality: 80,
format: ['webp', 'avif'],
screens: {
xs: 320, sm: 640, md: 768, lg: 1024, xl: 1280, xxl: 1536
}
}
})
(2) ▶ مثال: صور NuxtImg المتجاوبة
VUE
<template>
<div>
<!-- WebP/AVIF تلقائي + srcset متجاوب -->
<NuxtImg
src="/images/product-123.jpg"
width="800"
height="600"
format="webp"
loading="lazy"
sizes="sm:100vw md:50vw lg:33vw"
:modifiers="{ quality: 80 }"
alt="Premium Headphones"
/>
<!-- عنصر نائب مع ضبابية -->
<NuxtImg
src="/images/hero.jpg"
placeholder
width="1920"
height="1080"
format="webp"
alt="MegaShop Hero"
/>
</div>
</template>
الناتج:
TEXT
// التنفيذ ناجح
(3) ▶ مثال: NuxtPicture متعدد التنسيقات
VUE
<template>
<!-- NuxtPicture: اختيار تلقائي لأفضل تنسيق حسب المتصفح -->
<NuxtPicture
src="/images/banner.jpg"
width="1200"
height="400"
format="avif,webp"
loading="lazy"
alt="Sale Banner"
/>
</template>
الناتج:
TEXT
// التنفيذ ناجح
(1) مقارنة تنسيقات الصور
| التنسيق | نسبة الضغط | دعم المتصفح | حالات الاستخدام |
|---|---|---|---|
| AVIF | 🟢 الأفضل (50٪ مقابل JPG) | Chrome/Edge/Firefox | المتصفحات الحديثة |
| WebP | 🟢 عالية (30٪ مقابل JPG) | جميع المتصفحات الرئيسية | التنسيق المُوصى به للأغراض العامة |
| JPEG | 🟡 المرجع | الجميع | التوافق كبديل |
| PNG | 🔴 كبيرة | الجميع | الصور الشفافة |
5. استراتيجية التخزين المؤقت
(1) ▶ مثال: عناوين تخزين HTTP المؤقت
TYPESCRIPT
// nuxt.config.ts - عناوين التخزين المؤقت حسب المسار
export default defineNuxtConfig({
routeRules: {
// أصول ثابتة: تخزين مؤقت لسنة واحدة غير قابل للتغيير
'/_nuxt/**': {
headers: { 'cache-control': 'public, max-age=31536000, immutable' }
},
// الصور: تخزين مؤقت ليوم واحد
'/images/**': {
headers: { 'cache-control': 'public, max-age=86400, stale-while-revalidate=604800' }
},
// صفحات المنتجات: تخزين ISR مؤقت
'/products/**': {
swr: 86400,
headers: { 'cache-control': 'public, s-maxage=86400, stale-while-revalidate=3600' }
}
}
})
الناتج:
TEXT
// التنفيذ ناجح
(2) ▶ مثال: API تخزين Nitro KV المؤقت
TYPESCRIPT
// server/api/products/[id].get.ts - معالج مخزّن مؤقتاً
export default cachedEventHandler(
async (event) => {
const id = getRouterParam(event, 'id')
const product = await fetchProductFromDB(Number(id))
return product
},
{
maxAge: 60 * 60, // تخزين مؤقت لساعة واحدة
swr: true, // تقديم البيانات القديمة أثناء إعادة التحقق
getKey: (event) => `product:${getRouterParam(event, 'id')}`,
varies: ['Accept-Language'] // تخزين مؤقت مختلف لكل لغة
}
)
الناتج:
TEXT
// التنفيذ ناجح
(1) مقارنة مستويات التخزين المؤقت
| المستوى | الموقع | السرعة | طريقة التحكم |
|---|---|---|---|
| تخزين المتصفح المؤقت | جهاز المستخدم | ⚡⚡⚡ الأسرع | عنوان Cache-Control |
| تخزين حافة CDN المؤقت | أقرب عقدة | ⚡⚡ سريع | تكوين CDN + Vary |
| تخزين Nitro KV المؤقت | ذاكرة الخادم | ⚡ سريع | cachedEventHandler |
| تخزين صفحات ISR المؤقت | تخزين Nitro | ⚡ سريع | routeRules swr |
6. مثال شامل: تكوين تحسين أداء MegaShop
TYPESCRIPT
// nuxt.config.ts - تحسين أداء كامل
export default defineNuxtConfig({
ssr: true,
modules: ['@nuxt/image', '@nuxtjs/tailwindcss'],
// تحسين الصور
image: {
quality: 80,
format: ['webp', 'avif'],
screens: { xs: 320, sm: 640, md: 768, lg: 1024, xl: 1280 }
},
// تحسين بناء Vite
vite: {
build: {
cssCodeSplit: true,
rollupOptions: {
output: {
manualChunks(id) {
if (id.includes('node_modules')) {
if (id.includes('pinia')) return 'vendor-pinia'
if (id.includes('vue')) return 'vendor-vue'
return 'vendor-libs'
}
}
}
}
}
},
// استراتيجية التخزين المؤقت على مستوى المسار
routeRules: {
'/': { prerender: true },
'/products': { swr: 3600 },
'/products/**': { swr: 86400 },
'/admin/**': { ssr: false },
'/_nuxt/**': { headers: { 'cache-control': 'public, max-age=31536000, immutable' } },
'/images/**': { headers: { 'cache-control': 'public, max-age=86400' } }
},
// تلميحات أداء التطبيق
experimental: {
payloadExtraction: true // تقليل حجم الحمولة
}
})
VUE
<!-- pages/index.vue - الصفحة الرئيسية المحسّنة -->
<template>
<div>
<AppHeader />
<!-- فوق الطي: تحميل فوري -->
<section class="hero">
<NuxtImg src="/images/hero.jpg" width="1200" height="400" format="webp"
priority alt="MegaShop" />
<h1>MegaShop</h1>
</section>
<!-- تحت الطي: تحميل كسول -->
<LazyProductGrid :products="featured" />
<!-- شرطي: تحميل فقط عند الدخول في منطقة العرض -->
<LazyNewsletterSignup v-if="showNewsletter" />
</div>
</template>
<script setup lang="ts">
const { data: featured } = await useFetch('/api/products/featured')
const showNewsletter = ref(false)
</script>
❓ أسئلة شائعة
س ماذا أفعل إذا كانت درجة Lighthouse منخفضة؟
ج عالج المشاكل حسب الأولوية: 1) تحسين الصور (WebP/ضغط/تحميل كسول) → 2) تحليل حزمة JS (مكوّنات كسولة/اهتزاز الشجرة) → 3) استراتيجية التخزين المؤقت (عناوين HTTP/ISR) → 4) تحسين الخطوط.
س هل يدعم @nuxt/image عناوين URL للصور الخارجية؟
ج نعم، كوّن
image.domains لإضافة نطاقات خارجية: image: { domains: ['cdn.example.com'] }. NuxtImg سيحسّن الصور الخارجية تلقائياً.س ما الفرق بين NuxtImg ووسم
img العادي؟ج NuxtImg يُنشئ تلقائياً
srcset، ويحوّل الصور إلى WebP/AVIF، ويقصها لتناسب أبعاد الجهاز، ويفعّل التحميل الكسول. مع وسم img العادي، تحتاج لمعالجة كل هذه التحسينات يدوياً.س أيهما يأخذ الأسبقية: تخزين ISR المؤقت أم تخزين HTTP المؤقت؟
ج ISR هو تخزين مؤقت من جانب الخادم (مخزّن في Nitro)، بينما تخزين HTTP المؤقت يحدث على مستوى المتصفح/CDN. تدفق الطلب: تخزين المتصفح → تخزين CDN → Nitro ISR → عرض ديناميكي. كل طبقة تعمل بشكل مستقل.
س ما الفخاخ التي يجب الانتباه لها عند تكوين
manualChunks؟ج لا تضمّن كود جانب الخادم في مكوّنات العميل. Nuxt 3 يتولى تلقائياً فصل كود الخادم والعميل. يجب استخدام
manualChunks فقط لتقسيم المكتبات الخارجية.س هل من الصعب الوصول إلى درجة Lighthouse 90+؟
ج يمكنك الوصول إلى 90+ مع SSR وتحسين الصور واستراتيجية التخزين المؤقت. الجزء الأصعب هو إبقاء LCP أقل من 2.5 ثانية—يجب تبسيط مسار العرض الحرج وتحميل الموارد الأساسية فقط على الشاشة الأولى.
📖 ملخص
- تقسيم الكود: مكوّنات كسولة + استيراد ديناميكي + manualChunks؛ يُحمّل فقط الكود الضروري للشاشة الأولى
- تحسين الصور: @nuxt/image—تحويل تلقائي إلى WebP/AVIF، srcset متجاوب، وتحميل كسول
- استراتيجية التخزين المؤقت: المتصفح → CDN → Nitro ISR → عرض ديناميكي، تخزين مؤقت متعدد المستويات
- استخدم
rollup-plugin-visualizerلتحليل الحزمة لتحديد اختناقات الحجم - MegaShop بعد التحسين: JS 180 كيلوبايت (gzip) + LCP 1.3 ثانية + درجة Lighthouse 92
📝 تمارين
- تمرين أساسي (الصعوبة: ⭐): ثبّت @nuxt/image، واستبدل جميع وسوم
imgبـNuxtImg، وتحقق من تحويل WebP التلقائي. - تمرين متقدم (الصعوبة: ⭐⭐): استخدم
rollup-plugin-visualizerلتحليل الحزمة، وحدد أكبر تبعية، وكوّنmanualChunksلتقسيمها. - تحدٍ (الصعوبة: ⭐⭐⭐): حقّق درجة Lighthouse 90+: تحسين الصور + تقسيم الكود + استراتيجيات التخزين المؤقت، باستخدام Lighthouse CI للتدقيق الآلي
---|



