المكونات والتخطيطات
فريق Charlie يتكون من خمسة مطورين، وكل واحد يكتب المكونات بطريقة مختلفة—بعضهم يستوردها يدويًا، وآخرون يسجلونها عالميًا، ولا توجد اصطلاحات تسمية متسقة. لاحظت Alice أن الصفحة تحمّل الكثير من المكونات غير الضرورية، مما يبطئ تحميل الشاشة الأولى. أراد Bob استخدام تخطيط مختلف للوحة الإدارة لكنه لم يعرف كيف يبدل إليه.
1. ما ستتعلمه
- الاستيراد التلقائي للمكونات: قواعد مسح المجلدات واصطلاحات التسمية
- نظام التخطيطات: التبديل بين التخطيط الافتراضي والتخطيطات المخصصة
- التحميل الكسول للمكونات: بادئة "Lazy" و Suspense
- أفضل الممارسات لـ Props و Emits للمكونات
- MegaShop Header/Footer/ProductCard: دليل عملي
2. قصة حقيقية عن فريق
(1) نقطة الألم: إدارة مكونات فوضوية
فريق Charlie المكون من خمسة أفراد يعملون بمعزل: Alice تستورد كل مكون يدويًا، Bob يستخدم التسجيل العام مما يضخم حجم الحزمة، والزميل الجديد لا يستطيع معرفة أسماء المكونات. هناك ثلاث طرق مختلفة للإشارة إلى مكون ProductCard واحد عبر ملفات مختلفة.
(2) حل الاستيراد التلقائي في Nuxt 3
يقوم Nuxt 3 بمسح مجلد components/ تلقائيًا، وتوليد أسماء المكونات وفقًا للقواعد، وضمان إشارة الجميع إليها بطريقة متسقة:
<!-- لا حاجة للاستيراد، فقط استخدمه -->
<ProductCard :product="item" />
(3) الفوائد: توحيد + تقليل الحجم
وحّد الفريق طريقة الإشارة للمكونات؛ بادئة "Lazy" تضمن تحميل المكونات المرئية فقط على الشاشة الأولى، مما قلل حجم JavaScript للشاشة الأولى في MegaShop بنسبة 40%.
3. الاستيراد التلقائي للمكونات
(1) مسح المجلدات واصطلاحات التسمية
graph TB
A[components/] --> B[Root level]
A --> C[Sub-directory]
A --> D[Nested directory]
B --> E[ProductCard.vue → ProductCard]
C --> F[product/Card.vue → ProductCard ①]
D --> G[shop/product/Card.vue → ShopProductCard]
style G fill:#ffe,stroke:#f90
(2) مقارنة اصطلاحات التسمية
| مسار الملف | اسم المكون الافتراضي | pathPrefix: false |
|---|---|---|
| components/AppHeader.vue | AppHeader | AppHeader |
| components/product/Card.vue | ProductCard | Card |
| components/shop/ProductCard.vue | ShopProductCard | ProductCard |
| components/admin/user/Table.vue | AdminUserTable | Table |
(1) ▶ مثال: الاستيراد التلقائي للمكونات
<!-- components/ProductCard.vue -->
<template>
<div class="product-card">
<img :src="product.image" :alt="product.name" />
<h3>{{ product.name }}</h3>
<p>${{ product.price }} USD</p>
<button @click="$emit('add-to-cart', product)">Add to Cart</button>
</div>
</template>
<script setup lang="ts">
interface Product {
id: number
name: string
price: number
image: string
}
defineProps<{ product: Product }>()
defineEmits<{ 'add-to-cart': [product: Product] }>()
</script>
الناتج:
// Execution Successful
(2) ▶ مثال: استخدام المكون المستورد تلقائيًا على الصفحة
<!-- pages/products/index.vue -->
<template>
<div>
<h1>All Products</h1>
<div class="grid">
<!-- ProductCard مستورد تلقائيًا -->
<ProductCard
v-for="p in products"
:key="p.id"
:product="p"
@add-to-cart="handleAdd"
/>
</div>
</div>
</template>
<script setup lang="ts">
const { data: products } = await useFetch('/api/products')
function handleAdd(product: any) {
console.log('Added:', product.name)
}
</script>
الناتج:
// Execution Successful
4. نظام التخطيطات Layouts
(1) كيف يعمل التخطيط
graph TB
A[app.vue] --> B[NuxtLayout]
B --> C{Current Layout}
C -->|default| D[layouts/default.vue]
C -->|sidebar| E[layouts/sidebar.vue]
D --> F[NuxtPage - Page Content]
E --> F
(2) العلاقة بين app.vue والتخطيط
<!-- app.vue - المكون الجذري مع دعم التخطيط -->
<template>
<NuxtLayout>
<NuxtPage />
</NuxtLayout>
</template>
(1) ▶ مثال: التخطيط الافتراضي
<!-- layouts/default.vue -->
<template>
<div class="layout-default">
<AppHeader />
<main class="content">
<!-- محتوى الصفحة يُعرض هنا -->
<slot />
</main>
<AppFooter />
</div>
</template>
الناتج:
// Execution Successful
(2) ▶ مثال: تخطيط الشريط الجانبي
<!-- layouts/sidebar.vue -->
<template>
<div class="layout-sidebar">
<AppHeader />
<div class="main-wrapper">
<aside class="sidebar">
<nav>
<NuxtLink to="/admin">Dashboard</NuxtLink>
<NuxtLink to="/admin/products">Products</NuxtLink>
<NuxtLink to="/admin/orders">Orders</NuxtLink>
</nav>
</aside>
<main class="content">
<slot />
</main>
</div>
</div>
</template>
الناتج:
// Execution Successful
(3) ▶ مثال: تبديل تخطيط الصفحة
<!-- pages/admin/index.vue -->
<template>
<div>
<h1>Admin Dashboard</h1>
<p>Welcome, Bob</p>
</div>
</template>
<script setup lang="ts">
// التبديل إلى تخطيط الشريط الجانبي لهذه الصفحة
definePageMeta({
layout: 'sidebar'
})
</script>
الناتج:
// Execution Successful
(3) مقارنة التخطيط مقابل المسارات المتداخلة
| البُعد | التخطيط | المسارات المتداخلة |
|---|---|---|
| موقع التعريف | layouts/*.vue | pages/parent.vue |
| فتحة المحتوى | slot | NuxtPage |
| طريقة التبديل | definePageMeta | تلقائي (التسلسل الهرمي للتوجيه) |
| استرجاع البيانات | غير مستحسن | كل واحد يحصل على ما يحتاجه |
| حالات الاستخدام | تخطيط الصفحة العام | واجهة مستخدم مشتركة عبر مستويات التوجيه |
5. التحميل الكسول للمكونات
(1) آلية التحميل الكسول
يسجل Nuxt 3 تلقائيًا نسخة "Lazy" من كل مكون، حيث يُحمل الكود فقط عند تصيير المكون لأول مرة.
(1) ▶ مثال: التحميل الكسول ببادئة Lazy
<template>
<div>
<!-- تحميل فوري - مضمّن في الحزمة الأولية -->
<AppHeader />
<!-- تحميل كسول - تقسيم الكود، يُحمل عند أول تصيير -->
<LazyProductCard
v-for="p in products"
:key="p.id"
:product="p"
/>
<!-- Lazy مع v-if - يُحمل فقط عندما يكون الشرط صحيحًا -->
<LazyProductReviewModal
v-if="showReviewModal"
:product-id="selectedProductId"
@close="showReviewModal = false"
/>
</div>
</template>
الناتج:
// Execution Successful
(2) ▶ مثال: تحميل غير متزامن مع Suspense
<template>
<div>
<Suspense>
<template #default>
<LazyHeavyChart :data="chartData" />
</template>
<template #fallback>
<div class="loading">Loading chart...</div>
</template>
</Suspense>
</div>
</template>
الناتج:
// Execution Successful
(2) مقارنة استراتيجيات التحميل الكسول
| الاستراتيجية | التنفيذ | توقيت التحميل | السيناريوهات المناسبة |
|---|---|---|---|
| تحميل فوري | ProductCard | عند تحميل الصفحة | المكونات الأساسية على الشاشة الأولى |
| بادئة Lazy | LazyProductCard | عند أول تصيير | المكونات أسفل الطية |
| v-if + Lazy | LazyModal v-if | عندما يكون الشرط صحيحًا | نوافذ منبثقة/أدراج |
| Suspense | تغليف Suspense | عندما تكون العملية غير المتزامنة جاهزة | حالة "تحميل" مطلوبة |
6. أفضل الممارسات لـ Props و Emits
(1) ▶ مثال: Props و Emits آمنة النوع
<!-- components/ProductCard.vue -->
<script setup lang="ts">
interface Product {
id: number
name: string
price: number
image: string
inStock: boolean
}
// Props آمنة النوع مع قيم افتراضية
const props = withDefaults(
defineProps<{
product: Product
showStock?: boolean
currency?: string
}>(),
{
showStock: true,
currency: 'USD'
}
)
// Emits آمنة النوع
const emit = defineEmits<{
'add-to-cart': [product: Product]
'toggle-wishlist': [productId: number]
}>()
</script>
<template>
<div class="card">
<img :src="product.image" :alt="product.name" />
<h3>{{ product.name }}</h3>
<p>{{ product.price }} {{ currency }}</p>
<p v-if="showStock">
{{ product.inStock ? 'In Stock' : 'Out of Stock' }}
</p>
<button @click="emit('add-to-cart', product)">Add to Cart</button>
<button @click="emit('toggle-wishlist', product.id)">Wishlist</button>
</div>
</template>
الناتج:
// Execution Successful
7. مثال شامل: نظام مكونات تخطيط MegaShop
<!-- components/AppHeader.vue -->
<template>
<header class="app-header">
<NuxtLink to="/" class="logo">MegaShop</NuxtLink>
<nav>
<NuxtLink to="/products">Products</NuxtLink>
<NuxtLink to="/categories">Categories</NuxtLink>
</nav>
<div class="user-area">
<NuxtLink to="/cart">Cart ({{ cartCount }})</NuxtLink>
<NuxtLink v-if="isLoggedIn" to="/admin">Admin</NuxtLink>
<NuxtLink v-else to="/login">Sign In</NuxtLink>
</div>
</header>
</template>
<script setup lang="ts">
const cart = useState<any[]>('cart', () => [])
const cartCount = computed(() => cart.value.length)
const isLoggedIn = useState('isLoggedIn', () => false)
</script>
<!-- components/AppFooter.vue -->
<template>
<footer class="app-footer">
<p>© 2026 MegaShop. All rights reserved.</p>
<nav>
<NuxtLink to="/about">About</NuxtLink>
<NuxtLink to="/contact">Contact</NuxtLink>
<NuxtLink to="/privacy">Privacy Policy</NuxtLink>
</nav>
</footer>
</template>
<!-- layouts/default.vue -->
<template>
<div class="layout-default">
<AppHeader />
<main>
<slot />
</main>
<AppFooter />
</div>
</template>
❓ أسئلة شائعة
📖ملخص
- الاستيراد التلقائي للمكونات في Nuxt 3: يولّد أسماء المكونات بناءً على مسارات المجلدات، مما يلغي الحاجة للاستيراد اليدوي
- نظام التخطيطات يستخدم definePageMeta لتبديل تخطيطات الصفحة، و slot يحمل محتوى الصفحة
- بادئة "Lazy" تفعّل التحميل الكسول للمكونات، مما يقلل حجم JavaScript فوق الطية
- Props/Emits: تنفيذ أمان النوع باستخدام الأنواع العامة لـ TypeScript
- MegaShop يستخدم تخطيطين—"default" و "sidebar"—للواجهة الأمامية ولوحة الإدارة
📝تمارين
- تمرين أساسي (الصعوبة: ⭐): أنشئ مكوني AppHeader و AppFooter واستخدمهما في التخطيط الافتراضي.
- تمرين متقدم (الصعوبة: ⭐⭐): أنشئ تخطيط شريط جانبي وبدّل بين التخطيطين على صفحة الإدارة باستخدام definePageMeta. قارن نتائج التخطيطين.
- تحدٍ (الصعوبة: ⭐⭐⭐): استخدم بادئة Lazy و v-if لتنفيذ مكون نافذة منبثقة لتفاصيل المنتج يُحمل فقط عند النقر، واستخدم Suspense لعرض حالة التحميل.
---|



