دليل شامل للمبتدئين في نظام التوجيه (Routing) في Next.js
يُعد Next.js إطار عمل React قويًا وغنيًا بالميزات، مصممًا خصيصًا لتطوير تطبيقات الويب الجاهزة للإنتاج. إحدى أبرز هذه الميزات هي نظام التوجيه (Routing) الفعال الذي يعتمد على نظام الملفات، مما يبسط عملية إدارة المسارات في تطبيقك بشكل كبير. في هذا الدليل الشامل، سنغوص في أعماق كيفية عمل نظام التوجيه في Next.js، بدءًا من الأساسيات وصولًا إلى المفاهيم المتقدمة، لتمكينك من بناء تطبيقات React ديناميكية ومرنة.
كيف يعمل نظام التوجيه في Next.js؟
يعتمد Next.js على نظام الملفات لتمكين التوجيه في تطبيقات الويب. يقوم Next.js تلقائيًا بمعالجة كل ملف يحمل الامتدادات .js أو .jsx أو .ts أو .tsx داخل المجلد pages كمسار (Route) منفصل. ببساطة، كل صفحة في Next.js هي مكون React يرتبط مسارها باسم الملف الخاص بها. لنأخذ هيكل المجلدات التالي كمثال لتوضيح هذه الآلية:
├── pages
| ├── index.js
| ├── contact.js
| └── my-folder
| ├── about.js
| └── index.js
بناءً على هذا الهيكل، سنحصل على المسارات التالية:
- الملف
index.js: يمثل الصفحة الرئيسية (Home Page) ويمكن الوصول إليه عبرhttp://localhost:3000. - الملف
contact.js: يمثل صفحة “اتصل بنا” ويمكن الوصول إليه عبرhttp://localhost:3000/contact. - الملف
my-folder/index.js: يمثل الصفحة الرئيسية داخل المجلد الفرعيmy-folderويمكن الوصول إليه عبرhttp://localhost:3000/my-folder. - الملف
my-folder/about.js: يمثل صفحة “من نحن” داخل المجلد الفرعيmy-folderويمكن الوصول إليه عبرhttp://localhost:3000/my-folder/about.
الربط بين الصفحات في Next.js
لضمان سرعة التطبيق وتجربة مستخدم سلسة، يقوم Next.js افتراضيًا بإجراء “التهيئة المسبقة” (pre-rendering) لكل صفحة. لتحقيق الانتقال بين المسارات المختلفة، يوفر Next.js مكونًا خاصًا يُدعى Link، والذي يتم استيراده من next/link. يتيح هذا المكون للمستخدمين التنقل بين الصفحات دون الحاجة إلى إعادة تحميل الصفحة بالكامل، مما يحسن الأداء بشكل ملحوظ.
import Link from "next/link"
export default function IndexPage ( ) {
return (
< div >
< Link href = "/contact" >
< a > My second page </ a >
</ Link >
< Link href = "/my-folder/about" >
< a > My third page </ a >
</ Link >
</ div >
)
}
في المثال أعلاه، لدينا رابطان: الأول يقود إلى الصفحة http://localhost:3000/contact، والثاني إلى الصفحة http://localhost:3000/my-folder/about.
يستقبل المكون Link العديد من الخصائص (props)، ولكن الخاصية href هي الوحيدة المطلوبة. في هذا السياق، نستخدم وسم <a></a> كمكون ابن (child component) للربط بين الصفحات. ومع ذلك، يمكنك استخدام أي عنصر آخر يدعم حدث onClick مع المكون Link، مما يوفر مرونة كبيرة في التصميم والتفاعل.
تمرير معاملات المسار (Route Parameters)
يتيح Next.js إمكانية تمرير المعاملات (Parameters) عبر المسارات، ومن ثم استرجاع هذه البيانات داخل المكونات. يمكن تحقيق ذلك باستخدام الخطاف useRouter (للتوجيه من جانب العميل) أو الدالة getInitialProps (للتوجيه من جانب الخادم). كلاهما يوفران وصولًا إلى كائن الموجه (router object) الذي يحتوي على هذه المعاملات.
تمرير المعاملات من جانب العميل (Client-side)
لنفترض أننا نريد تمرير معرف (ID) إلى صفحة “حول”. يمكننا تعديل الملف index.js كما يلي:
import Link from "next/link"
export default function IndexPage ( ) {
return (
< Link
href = {{
pathname: "/about",
query: { id: "test" },
}}
>
< a > About page </ a >
</ Link >
)
}
كما يتضح هنا، بدلًا من تمرير سلسلة نصية بسيطة إلى الخاصية href، نقوم بتمرير كائن يحتوي على خاصية pathname (وهي المسار المستهدف) بالإضافة إلى عنصر query الذي يحمل البيانات المراد تمريرها (في هذه الحالة، id: "test").
استقبال المعاملات باستخدام useRouter
لاستقبال هذه البيانات في صفحة about.js، نستخدم الخطاف useRouter:
import { useRouter } from "next/router"
export default function AboutPage ( ) {
const router = useRouter()
const {
query: { id },
} = router
return < div > About us: {id} </ div >
}
هنا، نقوم باستيراد الخطاف useRouter للحصول على كائن الموجه، ثم نستخرج المعرف id من الكائن query باستخدام تقنية “التفكيك” (destructuring). هذا الأسلوب مثالي للتطبيقات التي تعتمد على التوجيه من جانب العميل.
استقبال المعاملات باستخدام getInitialProps (من جانب الخادم)
إذا كنت تستخدم “التهيئة المسبقة من جانب الخادم” (Server-Side Rendering – SSR)، فيجب عليك استخدام الدالة الثابتة getInitialProps لاستقبال البيانات. هذه الدالة تُنفذ على الخادم قبل عرض المكون:
export default function AboutPage ( { id } ) {
return < div > About us: {id} </ div >
}
AboutPage.getInitialProps = ( { query: { id } } ) => {
return { id }
}
في هذا المثال، تستقبل الدالة getInitialProps كائن السياق (context object) الذي يحتوي على الكائن query، ومنه نستخرج المعرف id ونعيده كخاصية للمكون AboutPage.
المسارات الديناميكية (Dynamic Routes)
يتميز Next.js بقدرته الفائقة على تعريف المسارات الديناميكية في تطبيقك باستخدام الأقواس المعقوفة ([param]). بدلًا من تحديد اسم ثابت لصفحاتك، يمكنك استخدام اسم ديناميكي يتغير بناءً على المحتوى أو البيانات. لننظر إلى هيكل المجلدات التالي كمثال:
├── pages
| ├── index.js
| ├── [slug].js
| └── my-folder
| ├── [id].js
| └── index.js
في هذا السيناريو، سيقوم Next.js بالتقاط معاملات المسار التي يتم تمريرها واستخدامها كجزء من اسم المسار الفعلي. هذا يفتح الباب أمام إنشاء صفحات متعددة باستخدام ملف واحد فقط.
إنشاء روابط للمسارات الديناميكية
لإنشاء روابط لهذه المسارات الديناميكية، سنقوم بتعديل الملف index.js كما يلي:
export default function IndexPage ( ) {
return (
< ul >
< li >
< Link href = "/" >
< a > Home </ a >
</ Link >
</ li >
< li >
< Link href = "/[slug]" as = "/my-slug" >
< a > First Route </ a >
</ Link >
</ li >
< li >
< Link href = "/my-folder/[id]" as = "/my-folder/my-id" >
< a > Second Route </ a >
</ Link >
</ li >
</ ul >
)
}
هنا، يجب علينا تحديد القيمة للخاصية as لأن المسار ديناميكي. ستكون قيمة المسار الفعلي هي ما تحدده في الخاصية as. على سبيل المثال، /[slug] سيصبح /my-slug.
استقبال المعاملات في المسارات الديناميكية
لاستقبال المعاملات في ملف [slug].js (الذي يمثل المسار الديناميكي /[slug])، يمكننا استخدام useRouter:
import { useRouter } from "next/router"
export default function DynamicPage ( ) {
const router = useRouter()
const {
query: { slug },
} = router
return < div > The dynamic route is {slug} </ div >
}
يمكنك الحصول على معاملات المسار بنفس الطريقة باستخدام الخطاف useRouter من جانب العميل، أو الدالة getInitialProps من جانب الخادم.
كمثال آخر، لاستقبال المعاملات في ملف my-folder/[id].js باستخدام getInitialProps:
export default function MyDynamicPage ( { id } ) {
return < div > My example is {id} </ div >
}
MyDynamicPage.getInitialProps = ( { query: { id } } ) => {
return { id }
}
هنا، استخدمنا getInitialProps لاستقبال قيمة المسار الديناميكي id وتمريرها كخاصية للمكون.
المسارات الديناميكية المتداخلة (Dynamic Nested Routes)
لا يقتصر دعم Next.js للمسارات الديناميكية على مستوى واحد فقط، بل يمكنك أيضًا إنشاء مسارات ديناميكية متداخلة باستخدام نفس صيغة الأقواس المعقوفة ([param]). يتيح لك هذا بناء هياكل مسارات معقدة ومرنة للغاية. لنأخذ هيكل الملفات التالي كمثال:
├── pages
| ├── index.js
| └── [dynamic]
| └── [id].js
في هذا الهيكل، لدينا مجلد ديناميكي [dynamic] بداخله ملف ديناميكي آخر [id].js. لإنشاء رابط لهذا المسار المتداخل، سنقوم بتعديل الملف index.js كما يلي:
export default function IndexPage ( ) {
return (
< ul >
< li >
< Link href = "/" >
< a > Home </ a >
</ Link >
</ li >
< li >
< Link href = "/[dynamic]/[id]" as = "/my-folder/my-id" >
< a > Dynamic nested Route </ a >
</ Link >
</ li >
</ ul >
)
}
كما ترى هنا، قمنا بتعيين القيم الديناميكية في الخاصية as تمامًا كما فعلنا في المثال السابق للمسارات الديناميكية البسيطة. الفرق الوحيد هو أننا في هذه الحالة، يجب علينا تحديد كل من اسم المجلد الديناميكي واسم الملف الديناميكي ضمن المسار.
استقبال المعاملات في المسارات الديناميكية المتداخلة
لاستقبال هذه المعاملات المتداخلة في ملف [id].js الموجود داخل المجلد [dynamic]، يمكننا استخدام الخطاف useRouter:
import { useRouter } from "next/router"
export default function DynamicPage ( ) {
const router = useRouter()
const {
query: { dynamic, id },
} = router
return (
< div > Data: {dynamic} - {id} </ div >
)
}
هنا، نقوم باستخراج كلا من المعاملين dynamic و id من الكائن query باستخدام الخطاف useRouter. هذا يوضح المرونة الكبيرة التي يوفرها Next.js في التعامل مع هياكل المسارات المعقدة.
الخلاصة التقنية
يُظهر نظام التوجيه في Next.js قوة وفلسفة إطار العمل في تبسيط مهام التطوير المعقدة. من خلال الاعتماد على نظام الملفات، يوفر Next.js طريقة بديهية وفعالة لإنشاء وإدارة المسارات، مما يقلل من الحاجة إلى إعدادات توجيه يدوية معقدة. إن مرونة المكون Link، وإمكانيات تمرير المعاملات عبر useRouter و getInitialProps، ودعم المسارات الديناميكية والمتداخلة، تجعل Next.js خيارًا ممتازًا لبناء تطبيقات React قابلة للتوسع والصيانة. هذه الميزات لا تعزز تجربة المطور فحسب، بل تضمن أيضًا تجربة مستخدم سريعة وسلسة بفضل التهيئة المسبقة للصفحات، مما يضع Next.js في طليعة أدوات تطوير الويب الحديثة.