الصفحات والمسارات
أثناء إضافة صفحات تفاصيل المنتجات إلى MegaShop، قام Bob بإعداد 50 مسار vue-router يدويًا. في كل مرة يُضاف منتج جديد، كان عليه تعديل ملفات المسارات—كان ذلك مؤلمًا حقًا. أخبره Charlie أن Nuxt 3 يولّد المسارات تلقائيًا بناءً على اصطلاحات الملفات، لذا إنشاء ملف هو نفسه إنشاء مسار.
1. ما ستتعلمه
- توجيه الملفات: القواعد الكاملة لتوجيه مجلد
pages/ - التوجيه الديناميكي: التقاط المعلمات في [id].vue و [...slug].vue
- المسارات المتداخلة والتخطيطات: صفحة أب + فتحات فرعية متداخلة
- التنقل البرمجي: navigateTo() / useRouter() / useRoute()
- دليل عملي للتوجيه الديناميكي متعدد المستويات في MegaShop
2. قصة حقيقية لمسؤول
(1) نقطة الألم: كابوس إعداد التوجيه اليدوي
أنشأ Bob 1,000 مسار لـ MegaShop باستخدام Vue Router التقليدي—واحد لكل فئة وكل صفحة منتج. في كل مرة يُضاف منتج جديد، كان عليه تعديل ملف المسار؛ وإذا فاته واحد، ظهرت أخطاء 404. لم تتمكن Alice من العثور على المنتجات، وارتفع معدل الشكاوى.
(2) حلول توجيه ملفات Nuxt
مع التوجيه القائم على الملفات في Nuxt 3، يحتاج Bob فقط لإنشاء ملف:
pages/products/[id].vue → /products/:id
مليون منتج في ملف واحد، مع تمرير المعلمات الديناميكية تلقائيًا.
(3) الفوائد: توجيه بدون إعداد
لم يعد Bob بحاجة لكتابة إعدادات المسارات—إضافة صفحة جديدة بسيطة كإنشاء ملف جديد، ومليون صفحة تفاصيل منتجات تحتاج فقط ملف [id].vue واحد.
3. قواعد توجيه الملفات
(1) عملية تعيين المجلدات إلى المسارات
flowchart LR
A[pages/index.vue] -->|"/"| B[Root Route]
C[pages/about.vue] -->|"/about"| C2[About Route]
D[pages/products/index.vue] -->|"/products"| D2[Product List Route]
E[pages/products/[id].vue] -->|"/products/:id"| E2[Product Detail Route]
F[pages/categories/[...slug].vue] -->|"/categories/:slug*"| F2[Catch-all Route]
(2) قواعد تعيين توجيه الملفات
| مسار الملف | المسار المُولّد | الوصف |
|---|---|---|
| pages/index.vue | / | الصفحة الرئيسية |
| pages/about.vue | /about | صفحة ثابتة |
| pages/products/index.vue | /products | قائمة المنتجات |
| pages/products/[id].vue | /products/:id | معلمة ديناميكية |
| pages/categories/[slug].vue | /categories/:slug | معلمات ديناميكية |
| pages/categories/[...slug].vue | /categories/:slug(*) | مسار شامل |
| pages/404.vue | /404 | 404 مخصص |
(1) ▶ مثال: توجيه الصفحات الأساسي
<!-- pages/index.vue -->
<template>
<div>
<h1>MegaShop - Millions of Products</h1>
<NuxtLink to="/products">Browse Products</NuxtLink>
</div>
</template>
الناتج:
// Execution Successful
<!-- pages/about.vue -->
<template>
<div>
<h1>About MegaShop</h1>
<p>Serving over 1 million products worldwide</p>
</div>
</template>
4. التوجيه الديناميكي
(1) توجيه ديناميكي بمعلمة واحدة
استخدم صياغة [param] لتعريف معلمات التوجيه الديناميكية.
(1) ▶ مثال: توجيه ديناميكي لصفحات تفاصيل المنتجات
<!-- pages/products/[id].vue -->
<template>
<div>
<h1>Product #{{ route.params.id }}</h1>
<p>Price: ${{ product?.price }} USD</p>
</div>
</template>
<script setup lang="ts">
const route = useRoute()
const { data: product } = await useFetch(`/api/products/${route.params.id}`)
</script>
الناتج:
// Execution Successful
(2) المسارات الشاملة
استخدم صياغة [...slug] لالتقاط مسارات متعددة المستويات.
(2) ▶ مثال: توجيه شامل لفئات متعددة المستويات
<!-- pages/categories/[...slug].vue -->
<template>
<div>
<h1>Category: {{ slugPath }}</h1>
<p>{{ products.length }} products found</p>
</div>
</template>
<script setup lang="ts">
const route = useRoute()
# /categories/electronics/headphones → slug = ['electronics', 'headphones']
const slugPath = computed(() => (route.params.slug as string[]).join('/'))
const { data: products } = await useFetch(`/api/categories/${slugPath.value}`)
</script>
الناتج:
// Execution Successful
(3) أنواع معلمات التوجيه الديناميكية
| الصياغة | المطابقة | مثال URL | قيمة params |
|---|---|---|---|
| [id] | قسم واحد | /products/123 | { id: '123' } |
| [id].vue + index.vue | اختياري | /products أو /products/123 | { id?: '123' } |
| [...slug] | شامل متعدد الأقسام | /a/b/c | { slug: ['a','b','c'] } |
5. المسارات المتداخلة
(1) هيكل التوجيه المتداخل
يتطلب التوجيه المتداخل صفحة أب (ملف .vue بنفس الاسم) ومجلد فرعي يعملان معًا:
pages/
├── products/
│ ├── index.vue # /products (فرعي)
│ ├── [id].vue # /products/:id (فرعي)
│ └── edit.vue # /products/edit (فرعي)
└── products.vue # /products (أب مع <NuxtPage />)
(1) ▶ مثال: مسارات متداخلة في صفحة المنتجات
<!-- pages/products.vue - تخطيط أب -->
<template>
<div>
<nav class="product-nav">
<NuxtLink to="/products">All Products</NuxtLink>
<NuxtLink to="/products/featured">Featured</NuxtLink>
</nav>
<!-- المسارات الفرعية تُعرض هنا -->
<NuxtPage />
</div>
</template>
الناتج:
// Execution Successful
<!-- pages/products/index.vue - فرعي: قائمة المنتجات -->
<template>
<div>
<h1>All Products</h1>
<p>1 million products available</p>
</div>
</template>
<!-- pages/products/[id].vue - فرعي: تفاصيل المنتج -->
<template>
<div>
<h1>Product Detail</h1>
<p>ID: {{ route.params.id }}</p>
</div>
</template>
(2) مقارنة المسارات المتداخلة مقابل التخطيط
| البُعد | المسارات المتداخلة | التخطيط |
|---|---|---|
| الآلية | صفحة أب + فتحة NuxtPage | layouts/ + NuxtLayout |
| علاقة التوجيه | مسارات أب-فرعية تشترك في التخطيطات | تبديل التخطيط على مستوى الصفحة |
| استرجاع البيانات | كل من الأب والفرعي يستخدمان useFetch |
التخطيط لا يسترجع البيانات |
| حالات الاستخدام | تسلسل فئة → منتج → مراجعة | تخطيط الصفحة العام (رأس/تذييل) |
6. التنقل البرمجي
(1) مقارنة ثلاث طرق للتنقل
| API | الغرض | متاح على الخادم | حالة الاستخدام |
|---|---|---|---|
| navigateTo() | تنقل برمجي | ✅ | إعادة توجيه بعد تسجيل الدخول |
| useRouter().push() | تنقل من جانب العميل | ❌ | تنقل بنقرة زر |
| NuxtLink | روابط تعريفية | ✅ | تنقل في القوالب |
(1) ▶ مثال: إعادة توجيه جانب الخادم بـ navigateTo
// في وسيط أو سياق جانب الخادم
export default defineNuxtRouteMiddleware((to) => {
const isAuthenticated = useState('isAuthenticated')
if (!isAuthenticated.value && to.path.startsWith('/admin')) {
return navigateTo('/login', { redirectCode: 302 })
}
})
الناتج:
// Execution Successful
(2) ▶ مثال: تنقل جانب العميل بـ useRouter
<template>
<button @click="goToProduct(product.id)">
View Details
</button>
</template>
<script setup lang="ts">
const router = useRouter()
function goToProduct(id: number) {
router.push(`/products/${id}`)
}
</script>
الناتج:
// Execution Successful
(3) ▶ مثال: استرجاع المعلمات بـ useRoute
<script setup lang="ts">
const route = useRoute()
# الوصول إلى المعلمات
const productId = route.params.id # من [id].vue
const categorySlug = route.params.slug # من [...slug].vue
# الوصول إلى معلمات الاستعلام
# /products?category=electronics&sort=price
const category = route.query.category # 'electronics'
const sort = route.query.sort # 'price'
</script>
الناتج:
// Execution Successful
7. دليل عملي للتوجيه متعدد المستويات في MegaShop
(1) خطة توجيه MegaShop
| URL | الملف | الوظيفة |
|---|---|---|
| / | pages/index.vue | الصفحة الرئيسية |
| /products | pages/products/index.vue | قائمة المنتجات |
| /products/[id] | pages/products/[id].vue | تفاصيل المنتج |
| /categories/[category] | pages/categories/[category].vue | صفحة الفئة |
| /categories/[category]/[subcategory] | pages/categories/[...slug].vue | الفئة الفرعية |
| /cart | pages/cart.vue | سلة التسوق |
| /checkout | pages/checkout.vue | صفحة الدفع |
| /admin | pages/admin/index.vue | لوحة الإدارة |
| /admin/products | pages/admin/products/index.vue | إدارة المنتجات |
| /404 | pages/404.vue | صفحة 404 |
(1) ▶ مثال: صفحة 404 لـ MegaShop
<!-- pages/404.vue -->
<template>
<div class="not-found">
<h1>404 - Product Not Found</h1>
<p>The product you're looking for doesn't exist.</p>
<NuxtLink to="/products">Browse all products</NuxtLink>
</div>
</template>
الناتج:
// Execution Successful
8. مثال شامل: نظام توجيه MegaShop
<!-- pages/index.vue - الصفحة الرئيسية -->
<template>
<div>
<section class="hero">
<h1>MegaShop</h1>
<p>Over 1 million products, delivered worldwide</p>
<NuxtLink to="/products">Shop Now</NuxtLink>
</section>
<section class="featured">
<ProductCard v-for="p in featured" :key="p.id" :product="p" />
</section>
</div>
</template>
<script setup lang="ts">
const { data: featured } = await useFetch('/api/products/featured')
</script>
<!-- pages/products/[id].vue - تفاصيل المنتج -->
<template>
<div v-if="product">
<h1>{{ product.name }}</h1>
<p class="price">${{ product.price }} USD</p>
<button @click="addToCart">Add to Cart</button>
<button @click="goBack">Back to List</button>
</div>
<div v-else>
<p>Product not found</p>
<NuxtLink to="/products">Browse Products</NuxtLink>
</div>
</template>
<script setup lang="ts">
const route = useRoute()
const router = useRouter()
const { data: product } = await useFetch(`/api/products/${route.params.id}`)
function addToCart() {
navigateTo('/cart')
}
function goBack() {
router.back()
}
</script>
❓ أسئلة شائعة
Number(route.params.id)، أو دع نوع إرجاع API يحدد القيمة عند استخدام useFetch.slug هو مصفوفة. المسارات الشاملة مناسبة للفئات بعدد مستويات غير محدود.products.vue والمجلد الفرعي products/. إذا كنت تريد المسار الفرعي فقط، لا تنشئ الصفحة الأب.a العادي؟a يؤدي لتحديث كامل للصفحة. استخدم NuxtLink للتنقل الداخلي ووسم a للروابط الخارجية.pages/404.vue. يعرض Nuxt 3 هذه الصفحة تلقائيًا عندما لا يتطابق أي مسار مع الطلب. يمكنك أيضًا معالجتها في مسار شامل.routeRules والمسارات؟routeRules في nuxt.config.ts لتحديد استراتيجيات التصيير (SSR/ISR/CSR) بناءً على مسارات URL؛ لا تؤثر على تعريف المسارات. المسارات تُعرّف بملفات مجلد pages/؛ routeRules يتحكم فقط في سلوك التصيير.📖ملخص
- توجيه ملفات Nuxt 3: الملفات تحت
pages/تُعيَّن تلقائيًا إلى مسارات بدون إعداد - التوجيه الديناميكي يستخدم [id].vue لالتقاط معلمة واحدة و [...slug].vue لالتقاط مسارات متعددة المستويات
- المسارات المتداخلة تتطلب صفحة أب بنفس الاسم + فتحة NuxtPage؛ هذا مفهوم مختلف عن التخطيط
- التنقل البرمجي: استخدم
navigateTo()على الخادم وuseRouter().push()على العميل - MegaShop: مليون منتج في ملف [id].vue واحد فقط
📝تمارين
- تمرين أساسي (الصعوبة: ⭐): أنشئ pages/about.vue و pages/contact.vue، وتحقق من توليد المسارات تلقائيًا.
- تمرين متقدم (الصعوبة: ⭐⭐): نفّذ توجيهًا ديناميكيًا ثنائي المستوى
/products/[category]/[id]واعرض معلمتي "الفئة" و "id" على الصفحة. - تحدٍ (الصعوبة: ⭐⭐⭐): نفّذ مسارًا شاملًا [...slug].vue يمكنه تصيير مسارات متعددة المستويات مثل
/docs/getting-started/installationوعرض شريط تنقل تفصيلي.
---|



