الصفحات والتوجيه
مقدمة إلى التوجيه القائم على الملفات
إحدى أقوى ميزات Next.js هي نظام التوجيه القائم على الملفات. على عكس تطبيقات React التقليدية حيث تحتاج إلى تكوين المسارات يدويًا باستخدام مكتبات مثل React Router، يقوم Next.js تلقائيًا بإنشاء مسارات بناءً على بنية الملفات في مجلد pages أو app.
يوفر هذا النهج عدة مزايا:
- لا حاجة لتكوين مكتبات التوجيه
- يتم تقسيم المسارات تلقائيًا للكود للحصول على أداء مثالي
- بنية الملفات تعكس مباشرة بنية URL
- سهل الفهم والصيانة
- يدعم المسارات الثابتة والديناميكية جاهزًا
Pages Router - التوجيه الأساسي
لنبدأ بـ Pages Router التقليدي لفهم الأساسيات. مع Pages Router، كل ملف في مجلد pages يصبح تلقائيًا مسارًا.
إنشاء صفحات أساسية
إليك كيف تُربط بنية الملفات بالمسارات:
pages/ ├── index.js → / ├── about.js → /about ├── contact.js → /contact └── blog.js → /blog
مثال على مكون صفحة بسيطة:
// pages/about.js
export default function About() {
return (
<div>
<h1>من نحن</h1>
<p>مرحبًا بكم في صفحة من نحن!</p>
</div>
)
}
هذا كل شيء! هذا الملف متاح تلقائيًا على http://localhost:3000/about.
المسارات المتداخلة مع المجلدات
يمكنك إنشاء مسارات متداخلة باستخدام المجلدات:
pages/
├── blog/
│ ├── index.js → /blog
│ ├── first-post.js → /blog/first-post
│ └── second-post.js → /blog/second-post
└── products/
├── index.js → /products
└── electronics.js → /products/electronics
// pages/blog/index.js
export default function Blog() {
return <h1>الصفحة الرئيسية للمدونة</h1>
}
// pages/blog/first-post.js
export default function FirstPost() {
return <h1>مقالتي الأولى</h1>
}
المسارات الديناميكية
تتيح لك المسارات الديناميكية إنشاء صفحات بأجزاء متغيرة في URL. هذا ضروري للمواقع المعتمدة على المحتوى حيث لا تعرف جميع عناوين URL المحتملة في وقت البناء.
إنشاء مسارات ديناميكية
استخدم الأقواس المربعة [] لإنشاء أجزاء مسار ديناميكية:
pages/
└── blog/
├── index.js
└── [slug].js → /blog/:slug (يطابق /blog/hello، /blog/world، إلخ.)
// pages/blog/[slug].js
import { useRouter } from 'next/router'
export default function BlogPost() {
const router = useRouter()
const { slug } = router.query
return (
<div>
<h1>مقالة المدونة: {slug}</h1>
<p>أنت تشاهد مقالة المدونة بالمعرف: {slug}</p>
</div>
)
}
الآن يمكنك الوصول إلى:
/blog/hello-world- سيكون slug هو "hello-world"/blog/nextjs-tutorial- سيكون slug هو "nextjs-tutorial"/blog/anything- سيكون slug هو "anything"
أجزاء ديناميكية متعددة
يمكنك الحصول على أجزاء ديناميكية متعددة في مسار واحد:
pages/
└── posts/
└── [category]/
└── [id].js → /posts/:category/:id
// pages/posts/[category]/[id].js
import { useRouter } from 'next/router'
export default function Post() {
const router = useRouter()
const { category, id } = router.query
return (
<div>
<h1>الفئة: {category}</h1>
<p>معرف المقالة: {id}</p>
</div>
)
}
هذا سيطابق عناوين URL مثل:
/posts/technology/123/posts/design/456/posts/marketing/789
مسارات الالتقاط الشامل
مسارات الالتقاط الشامل تطابق أجزاء مسار متعددة. إنها مثالية لبناء هياكل توجيه مرنة أو مواقع التوثيق.
مسار الالتقاط الشامل الأساسي
استخدم صيغة [...param] لالتقاط جميع أجزاء المسار:
pages/
└── docs/
└── [...slug].js → /docs/* (يطابق /docs/a، /docs/a/b، /docs/a/b/c، إلخ.)
// pages/docs/[...slug].js
import { useRouter } from 'next/router'
export default function Docs() {
const router = useRouter()
const { slug } = router.query
// slug هو مصفوفة من أجزاء المسار
// /docs/getting-started → ['getting-started']
// /docs/api/users/create → ['api', 'users', 'create']
return (
<div>
<h1>التوثيق</h1>
<p>أجزاء المسار: {slug?.join(' / ')}</p>
</div>
)
}
مسار الالتقاط الشامل الاختياري
لجعل مسار الالتقاط الشامل يطابق أيضًا المسار الأساسي، استخدم الأقواس المزدوجة:
pages/
└── docs/
└── [[...slug]].js → /docs، /docs/a، /docs/a/b، إلخ.
الآن سيتم مطابقة /docs أيضًا، مع كون slug يكون undefined.
App Router - النهج الحديث
App Router، المقدم في Next.js 13، يجلب نظام توجيه أكثر قوة ومرونة. يستخدم مجلد app بدلاً من pages.
الاختلافات الرئيسية عن Pages Router
- يستخدم ملفات
page.jsبدلاً من الملفات المسماة - يدعم مكونات خادم React افتراضيًا
- يتضمن تخطيطات وحالات تحميل وحدود أخطاء
- تحكم أكثر دقة في استراتيجيات العرض
بنية App Router الأساسية
app/
├── page.js → /
├── about/
│ └── page.js → /about
├── blog/
│ ├── page.js → /blog
│ └── [slug]/
│ └── page.js → /blog/:slug
└── products/
└── [id]/
└── page.js → /products/:id
لاحظ أن كل مسار هو مجلد يحتوي على ملف page.js:
// app/about/page.js
export default function AboutPage() {
return (
<div>
<h1>من نحن</h1>
<p>هذه صفحة من نحن باستخدام App Router</p>
</div>
)
}
المسارات الديناميكية في App Router
تعمل المسارات الديناميكية بشكل مشابه لكن تستخدم مجلدات بترميز الأقواس:
app/
└── blog/
├── page.js → /blog
└── [slug]/
└── page.js → /blog/:slug
// app/blog/[slug]/page.js
export default function BlogPost({ params }) {
// في App Router، يتم تمرير params كخصائص
return (
<div>
<h1>مقالة المدونة: {params.slug}</h1>
<p>قراءة المقالة بالمعرف: {params.slug}</p>
</div>
)
}
params، وليس من خلال useRouter(). يعمل المكون أيضًا على الخادم افتراضيًا، مما يجعله أكثر أداءً.
مسارات الالتقاط الشامل في App Router
app/
└── docs/
├── page.js → /docs
└── [...slug]/
└── page.js → /docs/* (أجزاء متعددة)
// app/docs/[...slug]/page.js
export default function DocsPage({ params }) {
const pathSegments = params.slug.join(' → ')
return (
<div>
<h1>التوثيق</h1>
<p>المسار الحالي: {pathSegments}</p>
</div>
)
}
التنقل بين المسارات
يوفر Next.js مكون Link للتنقل من جانب العميل بين المسارات. هذا أسرع بكثير من إعادة تحميل الصفحات التقليدية.
استخدام مكون Link
import Link from 'next/link'
export default function Navigation() {
return (
<nav>
<Link href="/">الرئيسية</Link>
<Link href="/about">من نحن</Link>
<Link href="/blog">المدونة</Link>
<Link href="/contact">اتصل بنا</Link>
</nav>
)
}
الروابط الديناميكية
import Link from 'next/link'
export default function BlogList({ posts }) {
return (
<ul>
{posts.map(post => (
<li key={post.id}>
<Link href={`/blog/${post.slug}`}>
{post.title}
</Link>
</li>
))}
</ul>
)
}
التنقل البرمجي
في بعض الأحيان تحتاج إلى التنقل برمجيًا (مثلاً بعد إرسال النموذج):
// Pages Router
import { useRouter } from 'next/router'
export default function LoginForm() {
const router = useRouter()
const handleSubmit = async (e) => {
e.preventDefault()
// معالجة تسجيل الدخول...
await router.push('/dashboard')
}
return <form onSubmit={handleSubmit}>...</form>
}
// App Router
'use client' // يجب أن يكون مكون عميل
import { useRouter } from 'next/navigation'
export default function LoginForm() {
const router = useRouter()
const handleSubmit = async (e) => {
e.preventDefault()
// معالجة تسجيل الدخول...
router.push('/dashboard')
}
return <form onSubmit={handleSubmit}>...</form>
}
next/router لـ Pages Router مقابل next/navigation لـ App Router. في App Router، تحتاج أيضًا إلى وضع علامة على المكونات التي تستخدم الخطافات كمكونات عميل باستخدام التوجيه 'use client'.
أولوية المسار والمطابقة
عندما يكون لديك مسارات متعددة يمكن أن تطابق URL، يتبع Next.js ترتيب أولوية محدد:
- المسارات المحددة مسبقًا: المسارات الثابتة مثل
/about - المسارات الديناميكية: المسارات مثل
/blog/[slug] - مسارات الالتقاط الشامل: المسارات مثل
/docs/[...slug]
pages/ ├── blog/ │ ├── index.js → يطابق /blog (أعلى أولوية) │ ├── create.js → يطابق /blog/create (الأولوية الثانية) │ ├── [slug].js → يطابق /blog/anything-else (الأولوية الثالثة) │ └── [...slug].js → لن يتم الوصول إليه بسبب [slug].js
معاملات الاستعلام
يمكنك الوصول إلى معاملات الاستعلام (الجزء بعد ? في عناوين URL) في صفحاتك:
// Pages Router - pages/search.js
import { useRouter } from 'next/router'
export default function Search() {
const router = useRouter()
const { q, category } = router.query
// URL: /search?q=nextjs&category=tutorial
// q = "nextjs"
// category = "tutorial"
return (
<div>
<h1>نتائج البحث</h1>
<p>الاستعلام: {q}</p>
<p>الفئة: {category}</p>
</div>
)
}
// App Router - app/search/page.js
export default function SearchPage({ searchParams }) {
// يتم تمرير searchParams كخصائص في App Router
const { q, category } = searchParams
return (
<div>
<h1>نتائج البحث</h1>
<p>الاستعلام: {q}</p>
<p>الفئة: {category}</p>
</div>
)
}
صفحة خطأ 404 مخصصة
يمكنك إنشاء صفحة 404 مخصصة عندما لا يوجد مسار:
// pages/404.js (Pages Router)
export default function Custom404() {
return (
<div>
<h1>404 - الصفحة غير موجودة</h1>
<p>الصفحة التي تبحث عنها غير موجودة.</p>
</div>
)
}
// app/not-found.js (App Router)
export default function NotFound() {
return (
<div>
<h1>404 - الصفحة غير موجودة</h1>
<p>الصفحة التي تبحث عنها غير موجودة.</p>
</div>
)
}
- أنشئ بنية مدونة أساسية بصفحة فهرس وما لا يقل عن 3 صفحات مقالات ثابتة
- حول مدونتك لاستخدام مسارات ديناميكية بملف [slug].js
- أنشئ قسم منتجات بمسارات متداخلة: /products/[category]/[id]
- ابنِ موقع توثيق باستخدام مسارات الالتقاط الشامل [...slug]
- أضف التنقل بين جميع صفحاتك باستخدام مكون Link
- أنشئ صفحة بحث تقرأ معاملات الاستعلام من URL
- نفذ التنقل البرمجي في نموذج يعيد التوجيه بعد الإرسال
- اختبر أولوية المسار بإنشاء مسارات ثابتة وديناميكية يمكن أن تتعارض
الخلاصة
نظام التوجيه القائم على الملفات في Next.js هو إحدى أقوى ميزاته، مما يجعل من السهل جدًا إنشاء وإدارة المسارات في تطبيقك. غطينا:
- المسارات الثابتة الأساسية باستخدام الملفات والمجلدات
- المسارات الديناميكية بترميز الأقواس لأجزاء URL المتغيرة
- المسارات المتداخلة لهياكل URL المعقدة
- مسارات الالتقاط الشامل لمطابقة المسارات المرنة
- الاختلافات بين Pages Router وApp Router
- التنقل باستخدام مكون Link والتوجيه البرمجي
- معاملات الاستعلام وصفحات الأخطاء المخصصة
فهم التوجيه أساسي لبناء تطبيقات Next.js. في الدرس القادم، سنستكشف الاختلافات بين App Router وPages Router بمزيد من التفاصيل، بما في ذلك التخطيطات وحالات التحميل ومعالجة الأخطاء.