Vue.js: Vue Router
آخر تحديث: 2026-10-20
Vue Router هو مدير التوجيه الرسمي لـ Vue — فهو يتيح لتطبيقك أحادي الصفحة (SPA) أن يحتوي على «صفحات» متعددة، بحيث يتوافق كل عنوان URL مع مكون معين. Vue Router 4 هو الإصدار المصمم خصيصًا لـ Vue 3، وهو يدعم واجهة برمجة التطبيقات (API) الخاصة بالتركيب (Composition API) بشكل كامل.
يُعد التعيين بين عناوين URL والمكونات جوهر عملية التوجيه في SPA. ويُعد إتقان استخدام Vue Router 4 مهارة أساسية لتطوير أي تطبيق Vue غير بسيط.
1. ما ستتعلمه
- تهيئة createRouter + createWebHistory
- 5 أنواع من المسارات: ثابتة، ديناميكية، متداخلة، مسماة، بديلة
- التنقل البرمجي (router.push / replace / go)
- التحميل المؤجل للمسارات (الاستيراد الديناميكي)
- معلمات المسار (params / query)
- حراس المسار (beforeEach / beforeEnter)
- 5 سيناريوهات رئيسية من واقع الحياة
2. «كابوس 404» في التطبيق أحادي الصفحة
(1) المشكلة: 5 صفحات، 5 حالات تكرار في الكود
تضمنت واجهة الإدارة الخاصة بـ«أليس» 5 صفحات (الصفحة الرئيسية/المنتجات/الطلبات/المستخدمون/الإعدادات):
4 أسئلة:
- عنوان URL لا يتغير: يؤدي تحديث الصفحة إلى فقدان الصفحة الحالية
- لا يمكن مشاركة الرابط: لا يمكنك نسخ عنوان URL لمشاركته مع زملائك
- عدم وجود أزرار «الأمام» و«الخلف» في المتصفح: تجربة مستخدم سيئة
- ضعف تحسين محركات البحث (SEO): محركات البحث لا تستطيع فهمه
(2) حل Vue Router 4: 5 مسارات + 5 مكونات
// router/index.js
import { createRouter, createWebHistory } from 'vue-router'
const routes = [
{ path: '/', component: () => import('@/views/HomePage.vue') },
{ path: '/products', component: () => import('@/views/ProductsPage.vue') },
{ path: '/orders', component: () => import('@/views/OrdersPage.vue') },
{ path: '/users', component: () => import('@/views/UsersPage.vue') },
{ path: '/settings', component: () => import('@/views/SettingsPage.vue') }
]
const router = createRouter({
history: createWebHistory(),
routes
})
export default router
5 مسارات + عرض جهاز توجيه واحد = تطبيق SPA كامل. تتم مزامنة عناوين URL تلقائيًا، وتعمل أزرار «الأمام» و«الخلف» في المتصفح كما هو متوقع.
(3) الإيرادات
بعد استخدام Vue Router:
- مزامنة عناوين URL: مزامنة تلقائية؛ يتم التحديث مع الاحتفاظ بالبيانات الموجودة
- قابلة للمشاركة: كل صفحة لها عنوان URL
- سجل المتصفح: تعمل أزرار «الأمام» و«الخلف» كما هو متوقع
- متوافق مع محركات البحث: دعم SSR (Nuxt 3)
- حجم الكود: 5 أوامر v-if → 5 أسطر في array المسارات (-80%)
3. تهيئة المسار
(1) كود التهيئة الكامل
// src/router/index.js
import { createRouter, createWebHistory, createWebHashHistory } from 'vue-router'
// 1. Define Routes(Lazy Loading)
const routes = [
{ path: '/', component: () => import('@/views/HomePage.vue') },
{ path: '/products', component: () => import('@/views/ProductsPage.vue') }
]
// 2. Create router Examples
const router = createRouter({
// history Mode: URL without # (Recommended)
history: createWebHistory(),
// hash Mode: URL with # (Compatibility with older browsers)
// history: createWebHashHistory(),
routes,
// Scrolling Behavior:Scroll to the top when switching routes
scrollBehavior(to, from, savedPosition) {
return savedPosition || { top: 0 }
}
})
export default router
(2) تسجيل ملف main.js
(3) مقارنة بين النموذجين الرئيسيين للتاريخ
| الجانب | createWebHistory | createWebHashHistory |
|---|---|---|
| تنسيق عنوان URL | /products |
/#/products |
| تحسين محركات البحث (SEO) | ✅ متوافق مع تحسين محركات البحث | ❌ غير متوافق مع تحسين محركات البحث |
| تكوين الخادم | مطلوب (try_files) | غير مطلوب |
| التوافق مع المتصفحات | IE10+ | IE8+ |
| التقييم | ⭐⭐⭐⭐⭐ | ⭐⭐ |
4. 5 أنواع من تكوينات التوجيه
(1) التوجيه الثابت
const routes = [
{ path: '/', component: () => import('@/views/HomePage.vue') },
{ path: '/about', component: () => import('@/views/AboutPage.vue') },
{ path: '/contact', component: () => import('@/views/ContactPage.vue') }
]
(2) التوجيه الديناميكي (المعلمات)
const routes = [
// /products/123
{
path: '/products/:id',
component: () => import('@/views/ProductDetailPage.vue')
},
// /users/456/posts/789
{
path: '/users/:userId/posts/:postId',
component: () => import('@/views/PostPage.vue')
},
// Optional Parameters
{
path: '/news/:category?',
component: () => import('@/views/NewsPage.vue')
}
]
<!-- ProductDetailPage.vue Receive Parameters -->
<script setup>
import { useRoute } from 'vue-router'
const route = useRoute()
console.log(route.params.id) // '123'
</script>
(3) المسارات المتداخلة
const routes = [
{
path: '/users',
component: () => import('@/views/UsersPage.vue'),
children: [
{
path: '', // Default Subroute
component: () => import('@/views/UsersListPage.vue')
},
{
path: ':id', // /users/123
component: () => import('@/views/UserDetailPage.vue')
}
]
}
]
(4) التوجيه المسمى
const routes = [
{
path: '/products/:id',
name: 'product-detail', // Named Routing
component: () => import('@/views/ProductDetailPage.vue')
}
]
<!-- Navigate via name, Avoid hard-coding URL -->
<router-link :to="{ name: 'product-detail', params: { id: 123 } }">
Product 123
</router-link>
(5) رمز البدل 404
const routes = [
{ path: '/', component: () => import('@/views/HomePage.vue') },
// ... Other Routes
{
path: '/:pathMatch(.*)*', // Match all undefined routes
component: () => import('@/views/NotFoundPage.vue')
}
]
▶ مثال: 5 أنواع من تكوينات التوجيه
const routes = [
// 1. Static Routing
{ path: '/', name: 'home', component: () => import('@/views/HomePage.vue') },
// 2. Dynamic Routing
{
path: '/products/:id',
name: 'product-detail',
component: () => import('@/views/ProductDetailPage.vue')
},
// 3. Nested Routes
{
path: '/admin',
component: () => import('@/views/AdminLayout.vue'),
meta: { requiresAuth: true },
children: [
{ path: '', name: 'admin-dashboard', component: () => import('@/views/DashboardPage.vue') },
{ path: 'products', name: 'admin-products', component: () => import('@/views/AdminProductsPage.vue') },
{ path: 'orders', name: 'admin-orders', component: () => import('@/views/AdminOrdersPage.vue') }
]
},
// 4. Named Routing
{
path: '/user/:userId',
name: 'user-profile',
component: () => import('@/views/UserProfilePage.vue')
},
// 5. 404
{ path: '/:pathMatch(.*)*', name: 'not-found', component: () => import('@/views/NotFoundPage.vue') }
]
الإخراج:
المسار / يفتح HomePage، و/products/:id يفتح صفحة التفاصيل مع params.id
/admin تعرض التخطيط مع صفحاتها الفرعية الثلاث، وأي مسار آخر يقع على not-found
كل مكوّنات المسارات تُحمَّل عند الطلب عبر import الديناميكي
▶ مثال: التحميل المؤجل للمسارات (التحميل حسب الطلب لـ 5 مكونات)
const routes = [
// ✅ Recommendations:News import
{ path: '/', component: () => import('@/views/HomePage.vue') },
{ path: '/products', component: () => import('@/views/ProductsPage.vue') }
]
الإخراج:
عند البناء تُفصل HomePage وProductsPage في ملفات JS مستقلة
أول زيارة لكل مسار تحمّل ملفه فقط، ثم يبقى في الذاكرة فلا يُعاد التحميل
5. 5 أنواع من أنظمة الملاحة
(1) التنقل التصريحي (router-link)
(2) التنقل البرمجي (router.push)
import { useRouter } from 'vue-router'
const router = useRouter()
function goToProduct(id) {
router.push(`/products/${id}`) // String
// or
router.push({ name: 'product-detail', params: { id } }) // Object
}
function goBack() {
router.back() // Back
}
function goForward() {
router.forward() // Forward
}
function replaceCurrent() {
router.replace('/login') // Replace(Leaving No Trace of History)
}
(3) 5 اختلافات بين "دفع" و"replace"
| الطريقة | السلوك | السجل |
|---|---|---|
router.دفع() |
الانتقال إلى صفحة جديدة | حفظ هذه الصفحة (يمكن العودة إليها) |
router.replace() |
استبدال الصفحة الحالية | دون ترك أي أثر (لا يمكن الرجوع) |
router.go(n) |
التقدم/التراجع n خطوات | عرض السجل |
router.back() |
تراجع خطوة إلى الوراء | استكشف التاريخ |
router.forward() |
خطوة إلى الأمام | جولة عبر التاريخ |
(4) مقارنة بين البرمجة الإجرائية والبرمجة التصريحية
| السيناريو | التوصية |
|---|---|
| ينقر المستخدم على الرابط | رابط الموجه (تصريحي) |
| التنقل الذي يتم تشغيله بواسطة JS | router.دفع (برمجياً) |
| إعادة التوجيه بعد إرسال النموذج | router.دفع |
| إعادة التوجيه بعد تسجيل الدخول | router.replace |
| إعادة التوجيه التلقائي 404 | router.back |
▶ مثال: 5 أنواع من التنقل
<!-- 1. String Path -->
<router-link to="/products">Products</router-link>
<!-- 2. Named Routing(Recommendations)-->
<router-link :to="{ name: 'products' }">Products</router-link>
<!-- 3. Dynamic Parameters -->
<router-link :to="{ name: 'product-detail', params: { id: 123 } }">
Product 123
</router-link>
<!-- 4. Query Parameters -->
<router-link :to="{ path: '/search', query: { q: 'vue' } }">
Search
</router-link>
<!-- 5. Programmatic Navigation -->
<button @click="goToProduct(123)">Go to Product</button>
import { useRouter } from 'vue-router'
const router = useRouter()
function goToProduct(id) {
router.push({ name: 'product-detail', params: { id } })
}
الإخراج:
النقر على Products ينتقل إلى /products، ومع params ينتقل إلى /product/123
مع query يصبح العنوان /search?q=vue
النقر على Go to Product(123) يستدعي router.push فينتقل إلى مسار التفاصيل
6. Route Guard (Navigation Guard)
(1) 3 أنواع من الحراس
// 1. Global Frontline Guard(Most Commonly Used)
router.beforeEach((to, from, next) => {
const isLoggedIn = !!localStorage.getItem('token')
if (to.meta.requiresAuth && !isLoggedIn) {
next('/login') // Skip Login
} else {
next() // Clearance
}
})
// 2. Dedicated Route Guard
const routes = [
{
path: '/admin',
component: () => import('@/views/AdminPage.vue'),
beforeEnter: (to, from, next) => {
if (isAdmin()) next()
else next('/403')
}
}
]
// 3. In-Component Guard
// Within the component:beforeRouteEnter / beforeRouteUpdate / beforeRouteLeave
(2) مثال كامل لحماية تسجيل الدخول
// router/index.js
router.beforeEach((to, from, next) => {
const token = localStorage.getItem('token')
const isLoggedIn = !!token
// Pages that require login
if (to.meta.requiresAuth && !isLoggedIn) {
return next({
name: 'login',
query: { redirect: to.fullPath } // Redirect after logging in
})
}
// Already logged in. Go to the login page.
if (to.name === 'login' && isLoggedIn) {
return next({ name: 'home' })
}
next()
})
// Routing meta Layout
const routes = [
{
path: '/dashboard',
component: () => import('@/views/DashboardPage.vue'),
meta: { requiresAuth: true } // You must log in
}
]
7. تمرين شامل: حارس تسجيل الدخول
▶ مثال: إكمال «Login Guard»
router.beforeEach((to, from, next) => {
const token = localStorage.getItem('token')
const isLoggedIn = !!token
if (to.meta.requiresAuth && !isLoggedIn) {
return next({ name: 'login', query: { redirect: to.fullPath } })
}
if (to.name === 'login' && isLoggedIn) {
return next({ name: 'home' })
}
next()
})
الإخراج:
بلا token: طلب /admin يعيد التوجيه إلى /login?redirect=/admin
مع token: تُفتح /admin، وزيارة /login تعيد التوجيه إلى الرئيسية
بقية المسارات تمر عبر next() دون تغيير
(1) 📊 5. مرجع سريع لـ 5 أخطاء شائعة
| الخطأ | الأعراض | الحل |
|---|---|---|
| لا توجد طريقة عرض جهاز التوجيه | لم يتم عرض الصفحة | إضافة <router-view /> |
| لا يتم تحميل المسارات بشكل مؤجل | بطء تحميل الشاشة الأولى | استخدم () => import() للاستيراد الديناميكي |
| Infinite Loop | لا يمكن الخروج | منع وظيفة next() من تشغيل beforeEach مرة أخرى |
| مسار متداخل بدون مخرج | الصفحة الفرعية غير معروضة | أضف <router-view /> إلى المكون الأصلي |
| 404 الطريق غير موجود | خطأ | إضافة path: '/:pathMatch(.*)*' |
(2) 📊 6. 5 مقارنات رئيسية في الأداء
| الوضع | الشاشة الأولى | التبديل | قابل للتطبيق |
|---|---|---|---|
| الاستيراد الثابت | بطيء | سريع | المشاريع الصغيرة |
| الاستيراد الديناميكي | ⭐⭐⭐⭐⭐ | بطيء عند التحميل الأول | موصى به |
| التحميل المؤجل لمجموعات التوجيه | ⭐⭐⭐⭐⭐ | بطء في التحميل الأول | المشاريع الكبيرة |
| التحميل المسبق | ⭐⭐⭐⭐ | سريع | تجربة المستخدم أولاً |
| SSR | ⭐⭐⭐⭐⭐ | سريع | Nuxt 3 |
❓ أسئلة شائعة
createWebHistory أم createWebHashHistory؟createWebHistory (عناوين URL التي لا تحتوي على #، وهي ملائمة لتحسين محركات البحث) تتطلب تهيئة الخادم؛ أما createWebHashHistory (عناوين URL التي تحتوي على #) فهي متوافقة مع المتصفحات القديمة. يُنصح باستخدام createWebHistory في بيئات الإنتاج.useRoute(): const route = useRoute(); route.params.id.router.push وrouter.replace؟push يُضيف إلى سجل المتصفح (مما يسمح للمستخدمين بالرجوع إلى الخلف)، بينما replace يستبدل الصفحة الحالية (دون ترك أي أثر في السجل). استخدم replace بعد تسجيل الدخول (نظرًا لأن المستخدمين لا يمكنهم العودة إلى صفحة تسجيل الدخول).beforeEach(async (to, from, next) => { await checkAuth(); next() }). لكن يجب توخي الحذر لتجنب الحلقات اللانهائية.children: []، وأضف <router-view /> إلى قالب المكون الرئيسي باعتباره نقطة الخروج للمسار الفرعي.meta: { requiresAuth: true } إلى تكوين المسار، واستخدم to.meta.requiresAuth في الحارس. تُستخدم هذه الحقول للأذونات والعناوين ومسار التنقل وغير ذلك.📖 ملخص
- Vue Router 4 هو مدير التوجيه الرسمي لـ Vue 3
- 5 أنواع من المسارات: ثابتة، ديناميكية، متداخلة، مسماة، بديلة
- 5 أنواع للتنقل: router-link، router.push، replace، back، forward
- 3 أنواع من المراقبين: عالمي (beforeEach)، خاص بالمسار (beforeEnter)، وداخلي للمكون
- يقلل التحميل المؤجل للمسارات (الاستيراد الديناميكي) من الوقت المستغرق حتى ظهور الصفحة الأولى
- يُوصى باستخدام
createWebHistory؛ أماcreateWebHashHistoryفهي مخصصة للتوافق مع الإصدارات القديمة من المتصفحات - المسارات المتداخلة مع العناصر التابعة +
<router-view />
📝 تمارين
-
أسئلة أساسية (مستوى الصعوبة: ⭐)
تنفيذ تطبيق SPA بسيط متعدد الصفحات:
- 3 صفحات: الصفحة الرئيسية / عن الموقع / الاتصال بنا
- شريط التنقل العلوي + عرض الموجه السفلي
- قم بتكوين جهاز التوجيه باستخدام
vue-router
-
مسائل متقدمة (مستوى الصعوبة: ⭐⭐)
تنفيذ توجيه منتجات التجارة الإلكترونية:
/productsصفحة القائمة/products/:idصفحة التفاصيل (المعلمات الديناميكية)/products/:id/editتعديل الصفحة- معالجة الخطأ 404
-
مسألة التحدي (مستوى الصعوبة: ⭐⭐⭐)
تنفيذ نظام توجيه كامل للخدمات الخلفية للتجارة الإلكترونية:
- 10 صفحات أو أكثر (الصفحة الرئيسية/المنتجات/الطلبات/المستخدمون/الإعدادات/تسجيل الدخول/404، إلخ)
- المسارات المتداخلة (/admin/dashboard، /admin/products، إلخ)
- التحميل المؤجل للمسارات (تحميل أسرع للشاشة الأولى)
- قم بتسجيل الدخول بصفة حارس (إذا لم تكن قد سجلت الدخول بعد، فانتقل إلى /login؛ وإذا كنت قد سجلت الدخول بالفعل، فارجع إلى هذه الصفحة)
- تغطية شاملة لخمس طرق للملاحة
- التنقل البرمجي (انقر على الزر للتنقل)
- تكوين المسارات ذات الأنواع القوية في TypeScript