React: البدء في استخدام React Router
آخر تحديث: 2026-08-26
يعمل توم على تطوير لوحة إدارة لموقع تجارة إلكترونية. في البداية، استخدم
useStateللتبديل بين «الصفحات» — متغير حالة واحد لكل من الصفحة الرئيسية وقائمة المنتجات وتفاصيل الطلب. ومع زيادة عدد الصفحات إلى 10، أصبحت منطقية الحالة معقدة للغاية، وظل شريط عنوان URL عالقًا عند/، مما جعل من المستحيل مشاركة رابط مباشر لصفحة معينة. فأدرك أنه يفتقر إلى حل توجيه احترافي.
1. ما ستتعلمه
- معايير الاختيار بين BrowserRouter و HashRouter
- آليات مطابقة المسارات لـ «Routes» و«Route»
- التنقل التصريحي باستخدام Link و NavLink
- useParams: يقرأ معلمات عناوين URL الديناميكية
- المسارات المتداخلة ووضع تخطيط المنافذ
2. الرسوم التخطيطية المفاهيمية
flowchart LR
A[BrowserRouter<br/>Routing Container] --> B[Routes<br/>Routing Table]
B --> C["Route path='/'<br/>→ Home"]
B --> D["Route path='/products'<br/>→ ProductList"]
B --> E["Route path='/products/:id'<br/>→ ProductDetail"]
B --> F["Route path='*'<br/>→ NotFound"]
C --> G[Rendering Component]
D --> G
E --> G
F --> G
style A fill:#e1f5fe,stroke:#0288d1
style B fill:#fff3e0,stroke:#f57c00
style G fill:#e8f5e9,stroke:#388e3c
يزور المستخدم عنوان URL مختلفًا → يقوم BrowserRouter بالتقاطه → تتم مطابقة المسار مع المسار الأنسب → يتم عرض المكون المقابل.
3. سيناريو واقعي
تحتاج لوحة إدارة توم إلى ثلاث صفحات رئيسية: «لوحة التحكم»، و«إدارة المنتجات»، و«إعدادات النظام». بالإضافة إلى ذلك، يتضمن قسم «إدارة المنتجات» صفحتين فرعيتين: «قائمة المنتجات» و«تفاصيل المنتج». ويريد توم أن يكون لكل صفحة عنوان URL فريد حتى يتمكن المستخدمون من التنقل باستخدام زري «الأمام» و«الخلف» في متصفحهم.
(1) اختيار وضع التوجيه
يوفر React Router وضعين للتوجيه، يختلفان اختلافًا جوهريًّا في طريقة تعاملهما مع عناوين URL:
| النمط | مثال على عنوان URL | المبدأ | حالات الاستخدام |
|---|---|---|---|
BrowserRouter |
example.com/users |
معالجة عناوين URL باستخدام واجهة برمجة تطبيقات السجل (History API) | المشاريع التي يمكن فيها للخادم تكوين إعادة كتابة عناوين URL |
HashRouter |
example.com/#/users |
منع طلبات الخادم الناتجة عن التغييرات في هاش عنوان URL | الاستضافة الثابتة (GitHub Pages، CDN) |
التوصيات: بالنسبة للمشاريع التي يمكن استضافتها على خادم، استخدم دائمًا BrowserRouter — فهذا ينتج عنه عنوان URL أكثر إيجازًا ويكون أكثر ملاءمةً لتحسين محركات البحث (SEO). أما بالنسبة للاستضافة الثابتة، فاستخدم HashRouter.
▶ المثال 1: التكوين الأساسي لتوجيه الشبكة
import { BrowserRouter, Routes, Route, Link } from 'react-router-dom'
function Home() {
return <h2>Dashboard Home Page</h2>
}
function ProductList() {
return <h2>Product List</h2>
}
function Settings() {
return <h2>System Settings</h2>
}
function NotFound() {
return <h2>404 — Page Not Found</h2>
}
function App() {
return (
<BrowserRouter>
<nav style={{ display: 'flex', gap: '1rem', padding: '1rem', background: '#f0f0f0' }}>
<Link to="/">Home</Link>
<Link to="/products">Product Management</Link>
<Link to="/settings">System Settings</Link>
</nav>
<main style={{ padding: '1rem' }}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/products" element={<ProductList />} />
<Route path="/settings" element={<Settings />} />
<Route path="*" element={<NotFound />} />
</Routes>
</main>
</BrowserRouter>
)
}
export default App
كيف تعمل: عند النقر على رابط تنقل، يتغير عنوان URL ويتم تحديث محتوى الصفحة، لكن المتصفح لا يعيد تحميل الصفحة بأكملها — وهذه هي جوهر تجربة التوجيه في تطبيقات SPA.
حول path="*": يتطابق الرمز البدل * مع جميع المسارات غير المحددة في المسارات السابقة؛ وعادةً ما يتم وضعه في نهاية قائمة المسارات لإنشاء صفحة خطأ 404. في React Router الإصدار 6، لا يمكن أن يظهر * إلا كحرف أخير في المسار.
(2) حالة تفعيل NavLink
| مكون التنقل | الغرض | أسلوب التنشيط | حالات الاستخدام |
|---|---|---|---|
<Link to="/path"> |
التنقل التصريحي | لا شيء | روابط التنقل العامة |
<NavLink to="/path"> |
التنقل مع الحالة النشطة | isActive الاستدعاء المرتد |
تمييز الشريط الجانبي/شريط التنقل العلوي |
navigate('/path') |
عمليات إعادة التوجيه البرمجية | — | عمليات إعادة التوجيه بعد تسجيل الدخول، وعمليات إعادة التوجيه بعد إرسال النموذج |
<Navigate to="/path" /> |
إعادة التوجيه التصريحي | — | مكون إعادة التوجيه الشرطي |
يريد توم تمييز الصفحة الحالية في شريط التنقل حتى يتمكن المستخدمون من معرفة «موقعهم» بوضوح. يوفر المكون <NavLink> المعلمة isActive، التي تتيح لك تعيين الأنماط ديناميكيًا بناءً على المسار الحالي. ويحتوي على خاصيتين إضافيتين مقارنةً بـ <Link>: تدعم كل من style وclassName قبول دالة استدعاء مرتد مع الخاصيتين isActive وisPending.
▶ المثال 2: شريط التنقل بنمط نشط
import { NavLink } from 'react-router-dom'
function NavBar() {
const linkStyle = {
padding: '8px 16px',
textDecoration: 'none',
borderRadius: '6px',
transition: 'all 0.2s'
}
const activeStyle = {
...linkStyle,
backgroundColor: '#1976d2',
color: '#fff',
fontWeight: 'bold'
}
const inactiveStyle = {
...linkStyle,
color: '#333'
}
return (
<nav style={{ display: 'flex', gap: '12px', padding: '12px', background: '#fafafa' }}>
<NavLink
to="/"
style={({ isActive }) => (isActive ? activeStyle : inactiveStyle)}
end // Exact Match,Avoid "/" Match all that start with "/" Starting path
>
Home
</NavLink>
<NavLink
to="/products"
style={({ isActive }) => (isActive ? activeStyle : inactiveStyle)}
>
Product Management
</NavLink>
<NavLink
to="/settings"
style={({ isActive }) => (isActive ? activeStyle : inactiveStyle)}
>
System Settings
</NavLink>
</nav>
)
}
النقطة الأساسية: تضمن الخاصية end أن المسار / لا يتم تنشيطه إلا في حالة وجود تطابق تام؛ وإلا، فإن جميع المسارات ستؤدي إلى تفعيل نمط الصفحة الرئيسية. كما تدعم الخاصية className التابعة لـ NavLink وظائف الاستدعاء المرتد، مما يجعلها مناسبة للمشاريع التي تستخدم أسماء فئات CSS.
(3) مشاركة المسارات والتخطيطات المتداخلة
اكتشف توم أن صفحة إدارة المنتجات تحتوي على صفحتين فرعيتين — «قائمة المنتجات» و«إضافة منتج» — اللتين تشتركان في نفس تخطيط الشريط الجانبي. إن كتابة كود التخطيط مرارًا وتكرارًا لكل صفحة فرعية سيكون أمرًا زائدًا عن الحاجة ويصعب صيانته. ويحل التوجيه المتداخل في React Router، بالاقتران مع <Outlet>، هذه المشكلة بشكل مثالي.
يتمثل المفهوم الأساسي للتوجيه المتداخل في أن المسار الأصلي يحدد إطار التخطيط، بينما يقوم المسار الفرعي بإدراج المحتوى عبر منافذ. ولا يهتم المكون الأصلي بما يعرضه المسار الفرعي على وجه التحديد؛ فهو مسؤول حصريًّا عن إطار التخطيط.
▶ المثال 3: المسارات والمنافذ المتداخلة
import { BrowserRouter, Routes, Route, Link, Outlet, useParams } from 'react-router-dom'
// Parent Layout Component — Shared Sidebar + Outlet
function ProductsLayout() {
return (
<div style={{ display: 'flex' }}>
<aside style={{ width: '200px', padding: '16px', background: '#f5f5f5' }}>
<h3>Product Management</h3>
<nav style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
<Link to="list">Product List</Link>
<Link to="add">Add Item</Link>
</nav>
</aside>
<main style={{ flex: 1, padding: '16px' }}>
{/* The components of the child route are rendered here */}
<Outlet />
</main>
</div>
)
}
function ProductList() {
const products = [
{ id: 1, name: 'React Programming Books', price: 79 },
{ id: 2, name: 'TypeScript Guide', price: 59 },
{ id: 3, name: 'Node.js Real-World Experience', price: 69 }
]
return (
<div>
<h2>Product List</h2>
<ul>
{products.map(p => (
<li key={p.id}>
<Link to={`/products/detail/${p.id}`}>
{p.name} — ${p.price}
</Link>
</li>
))}
</ul>
</div>
)
}
function AddProduct() {
return (
<div>
<h2>Add Item</h2>
<form onSubmit={e => { e.preventDefault(); alert('Submission Successful!') }}>
<div><label>Product Name:<input name="name" /></label></div>
<div><label>Price:<input name="price" type="number" /></label></div>
<button type="submit">Submit</button>
</form>
</div>
)
}
function ProductDetail() {
const { id } = useParams()
return <h2>Product Details(ID:{id})</h2>
}
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<h2>Home</h2>} />
{/* Nested Routes:Parent Route with Layout,Subnet routing is enabled Outlet Rendering */}
<Route path="/products" element={<ProductsLayout />}>
<Route index element={<ProductList />} /> {/* /products Default Display */}
<Route path="list" element={<ProductList />} /> {/* /products/list */}
<Route path="add" element={<AddProduct />} /> {/* /products/add */}
<Route path="detail/:id" element={<ProductDetail />} /> {/* /products/detail/1 */}
</Route>
<Route path="*" element={<h2>404 Not found</h2>} />
</Routes>
</BrowserRouter>
)
}
منطق التدفق: زيارة /products/list → ProductsLayout (عرض الشريط الجانبي) → Outlet (عرض مكون ProductList في ذلك الموقع). تتطابق بنية عناوين URL بشكل مباشر مع بنية المكونات، مما يجعلها واضحة وسهلة الصيانة.
حول مسار الفهرس: يُعد مسار index المسار الفرعي الافتراضي للمسار الأصلي. عند الوصول إلى /products وعدم وجود مسار فرعي مطابق (لا list ولا add)، يظهر محتوى مسار الفهرس في «Outlet». وهذا يضمن عدم ظهور منطقة فارغة في المسار الأصلي.
4. التوجيه الديناميكي ومعلمات المسار
| نمط المسار | مثال على عنوان URL | قيمة الإرجاع لـ useParams | الوصف |
|---|---|---|---|
/users/:id |
/users/42 |
{ id: '42' } |
معلمة ديناميكية واحدة |
/users/:userId/posts/:postId |
/users/42/posts/99 |
{ userId: '42', postId: '99' } |
المعلمات الديناميكية متعددة المراحل |
/files/* |
/files/a/b/c |
{ '*': 'a/b/c' } |
حرف البدل الذي يطابق المسار المتبقي |
/categories/:catId/:tab? |
يجب تحديد مسارين | { catId, tab } |
معلمات اختيارية (لا يوجد دعم أصلي) |
يُعد التوجيه الديناميكي إحدى أهم ميزات نظام التوجيه. تحتاج صفحة تفاصيل المنتج الخاصة بـ«توم» إلى عرض محتوى مختلف بناءً على معرّف المنتج، ولا يمكنه بأي حال من الأحوال كتابة مسار لكل معرّف منتج — وهنا يأتي دور صيغة :param لتعريف المسارات الديناميكية.
(1) استخدام useParams
:id هو رمز مؤقت لمعلمة ديناميكية؛ ويتم استخراج القيمة الفعلية من عنوان URL عبر الرابط useParams.
function UserDetail() {
const { userId, postId } = useParams()
return <p>User {userId} Article {postId}</p>
}
// URL: /users/42/posts/99 → userId=42, postId=99
▶ المثال 4: صفحة تفاصيل المنتج
import { useParams, Link, useNavigate } from 'react-router-dom'
// Simulated Product Data
const products = [
{ id: '1', name: 'React Programming Books', price: 79, description: 'Mastering It from Scratch React 18 Development' },
{ id: '2', name: 'TypeScript Guide', price: 59, description: 'Systematic Study TypeScript Type System' },
{ id: '3', name: 'Node.js Real-World Experience', price: 69, description: 'Backend Development: From Beginner to Advanced' }
]
function ProductDetail() {
const { id } = useParams()
const navigate = useNavigate()
const product = products.find(p => p.id === id)
if (!product) {
return (
<div>
<h2>The product does not exist.</h2>
<button onClick={() => navigate('/products')}>Back to Product List</button>
</div>
)
}
return (
<div>
<h2>{product.name}</h2>
<p className="price">Price:${product.price}</p>
<p className="desc">{product.description}</p>
<Link to="/products">← Back to List</Link>
</div>
)
}
ملاحظة: عند استخدام المعلمات الديناميكية، يجب مراعاة الحالات التي «لا توجد فيها بيانات مقابلة لقيمة المعلمة». يتعامل الفرع if (!product) في الكود أعلاه مع البيانات الفارغة لمنع ظهور شاشة فارغة عند محاولة الوصول إلى معرّف غير موجود.
(2) المعلمات الديناميكية متعددة الأجزاء
يمكن أن يحتوي المسار على عدة معلمات ديناميكية، وهو أمر شائع في السيناريوهات التي تتضمن موارد متداخلة:
// Route Definitions
<Route path="/categories/:catId/products/:prodId" element={<ProductView />} />
// Extracted from the component
function ProductView() {
const { catId, prodId } = useParams()
// URL: /categories/electronics/products/42
// catId = "electronics", prodId = "42"
return <h2>Categories {catId} Items under {prodId}</h2>
}
(3) المعلمات الاختيارية وأحرف البدل
لا يدعم React Router v6 المعلمات الاختيارية بشكل مباشر، ولكن يمكنك تحقيق نتيجة مشابهة بطريقتين:
// Method 1:Define two Route(Recommendations)
<Route path="/categories/:catId" element={<CategoryPage />} />
<Route path="/categories/:catId/:tab" element={<CategoryPage />} />
// Method 2:Make the determination within the component itself
function CategoryPage() {
const { catId, tab } = useParams()
const activeTab = tab || 'overview' // Default value
return <h2>Categories {catId} - {activeTab}</h2>
}
تتمثل ميزة الخيار 1 في أن عنوان URL واضح من الناحية الدلالية، في حين أن الخيار 2 أكثر إيجازًا ولكنه يقلل من سهولة قراءة عنوان URL.
(4) شرح مفصل لأولويات مطابقة المسارات
تعتمد مطابقة المسارات في React Router v6 على خوارزمية التقييم، بدلاً من نهج «من يأتي أولاً يخدم أولاً» المستخدم في الأطر التقليدية. ويمكن أن يساعد فهم هذه القواعد في حل المشكلات التي لا تعمل فيها المسارات كما هو متوقع.
// Suppose we have the following routing configuration
<Routes>
<Route path="/products/new" element={<NewProduct />} /> {/* Static Path */}
<Route path="/products/:id" element={<ProductDetail />} /> {/* Dynamic Path */}
<Route path="/products/:id/edit" element={<EditProduct />} /> {/* Hybrid Path */}
</Routes>
أولوية المطابقة (من الأعلى إلى الأدنى):
- مقاطع المسار الثابتة (
new) لها الأسبقية على مقاطع المعلمات الديناميكية (:id) - المسارات التي تحتوي على عدد أكبر من المقاطع الثابتة لها الأسبقية على المسارات التي تحتوي على عدد أقل من المقاطع الثابتة
- المسارات التي تحتوي على عدد أكبر من المقاطع لها الأسبقية على تلك التي تحتوي على عدد أقل من المقاطع.
وبالتالي، فإن الطلب الموجه إلى /products/new يتطابق مع <NewProduct />، والطلب الموجه إلى /products/42 يتطابق مع <ProductDetail />، والطلب الموجه إلى /products/42/edit يتطابق مع <EditProduct />. ولا داعي للمطورين للقلق بشأن الترتيب، حيث يختار النظام تلقائيًا «أفضل تطابق».
❓ أسئلة شائعة
/products/detail/1 من الخادم، لكن هذا الملف غير موجود على الخادم. الحل: قم بتكوين try_files $uri $uri/ /index.html في Nginx لإعادة توجيه جميع طلبات المسار إلى index.html، ودع React Router يتولى عملية المطابقة في الواجهة الأمامية. إذا لم تتمكن من تكوين الخادم، فانتقل إلى استخدام HashRouter (لن يتم إرسال الجزء الذي يلي # إلى الخادم).<Link> والعلامة الأصلية <a>؟<a> إلى تحديث الصفحة بالكامل في المتصفح، مما يتسبب في فقدان تطبيق SPA لكل حالاته المخزنة في الذاكرة. أما <Link> فتمنع التنقل الافتراضي، وتقوم بتحديث عنوان URL عبر واجهة برمجة التطبيقات (API) الخاصة بسجل التصفح، وتُعلم React Router بعرض مكون جديد — دون تحديث الصفحة أو فقدان الحالة. في تطبيقات SPA، استخدم دائمًا Link أو NavLink بدلاً من <a>./users/new أكثر خصوصية من /users/:id، وبالتالي له الأسبقية. يعمل * كحرف بدل يطابق جميع المسارات غير المطابقة، وعادةً ما يتم وضعه في النهاية ليكون بمثابة صفحة خطأ 404. تستخدم المسارات مطابقة البادئة بشكل افتراضي؛ وإضافة السمة end تغير هذا إلى مطابقة تامة.<Route index element={...} /> يحدد المسار الفرعي الافتراضي للمسار الأصلي. عند الوصول إلى المسار الأصلي نفسه (على سبيل المثال، /products)، إذا كان المسار الأصلي يستخدم منفذًا (Outlet)، فسيتم عرض محتوى مسار "index" في المنفذ. وهو يعمل كـ"الصفحة الافتراضية ضمن المسار الأصلي"، مما يمنع ظهور مساحة فارغة عند الوصول إلى المسار الأصلي.<Routes> + <Route> يحل محل <Switch>، ويقوم مكون Route تلقائيًا بمطابقة المسار الأكثر تحديدًا؛ ② تستخدم المسارات المتداخلة الآن <Outlet> بدلاً من عرض المسارات الفرعية يدويًّا؛ ③ useNavigate() يحل محل useHistory()—navigate('/path') يحل محل history.push('/path')، وnavigate(-1) يحل محل history.goBack(). واجهة برمجة التطبيقات (API) للإصدار v6 أكثر إيجازًا، لكن عملية الترحيل تتطلب تغييرات كبيرة في الكود.(5) المسارات النسبية مقابل المسارات المطلقة
في التوجيه المتداخل، يكون المسار الخاص بـ <Link to="..."> مسارًا نسبيًا بالنسبة للمسار الحالي، في حين أن <Link to="/..."> هو مسار مطلق. وفهم هذا الفرق أمر بالغ الأهمية لتجنب أخطاء التنقل.
// Currently in /products under (ProductsLayout Within the component)
<Link to="list"> {/* → /products/list(Relative Path,After appending it to the current route) */}
<Link to="/list"> {/* → /list(Absolute Path,Replace directly) */}
<Link to="../settings"> {/* → /settings(Parent-level relative path) */}
في التوجيه المتداخل، إذا كان المسار الفرعي موجودًا داخل ProductsLayout، فيجب أن تستخدم جميع الروابط الموجودة بداخله مسارات نسبية (بدون / في البداية). وهذا يضمن أن الروابط في المسار الفرعي تتكيف تلقائيًا عند تغير مسار المسار الأصلي.
5. صفحات الخطأ 404 وأنماط تصميم التوجيه
(1) التوجيه باستخدام أحرف البدل
path="*" يطابق جميع المسارات غير المحددة، وهو الطريقة القياسية لتنفيذ صفحة 404. ومع ذلك، في React Router الإصدار 6، لا يمكن أن يظهر * إلا في نهاية المسار، ولا يمكن استخدامه مثل path="/users/*/edit".
function App() {
return (
<Routes>
<Route path="/" element={<Home />} />
<Route path="/products" element={<ProductList />} />
<Route path="/products/:id" element={<ProductDetail />} />
<Route path="/about" element={<About />} />
{/* Wildcard routes must be placed last */}
<Route path="*" element={<NotFound />} />
</Routes>
)
}
function NotFound() {
return (
<div style={{ textAlign: 'center', padding: '40px' }}>
<h1>404</h1>
<p>Sorry, the page you are looking for does not exist.。</p>
<Link to="/">Back to Home</Link>
</div>
)
}
(2) الاستخدام الأساسي لـ useNavigate
على الرغم من أن useNavigate سيتم تناوله بالتفصيل في الدورة المتقدمة، إلا أن توم سيحتاج أيضًا إلى استخدامه في حالات معينة خلال مرحلة المبتدئين — مثل إعادة التوجيه بعد انتهاء العد التنازلي لصفحة تسجيل الدخول أو إعادة التوجيه بعد إرسال نموذج.
import { useNavigate } from 'react-router-dom'
function OrderSuccess() {
const navigate = useNavigate()
const [countdown, setCountdown] = useState(5)
useEffect(() => {
const timer = setInterval(() => {
setCountdown(prev => {
if (prev <= 1) {
clearInterval(timer)
navigate('/orders') // Automatically redirect when the countdown ends
return 0
}
return prev - 1
})
}, 1000)
return () => clearInterval(timer)
}, [navigate])
return (
<div>
<h2>Order Placed Successfully!</h2>
<p>{countdown} You will be automatically redirected to the order page in seconds.</p>
<button onClick={() => navigate('/orders')}>Check it out now</button>
</div>
)
}
▶ المثال 5: لوحة إدارة تجمع بين جميع الميزات
اجمع كل المفاهيم التي تعلمتها في هذا الدرس — BrowserRouter، والمسارات (Routes)، والمسارات المتداخلة، وNavLink، وuseParams، وصفحات الخطأ 404 — لإنشاء الهيكل الأساسي للوحة تحكم إدارية كاملة.
import { BrowserRouter, Routes, Route, NavLink, Outlet, useParams } from 'react-router-dom'
import './App.css'
// Layout Components
function AdminLayout() {
return (
<div className="admin-container">
<header className="admin-header">
<h1>Tom E-commerce Management Backend</h1>
</header>
<div className="admin-body">
<nav className="admin-sidebar">
<NavLink to="/" end>Dashboard</NavLink>
<NavLink to="/products">Product Management</NavLink>
<NavLink to="/orders">Order Management</NavLink>
<NavLink to="/settings">System Settings</NavLink>
</nav>
<main className="admin-content">
<Outlet />
</main>
</div>
</div>
)
}
// Page Components
function Dashboard() {
return <h2>Welcome back,Tom!Number of Orders Today:42</h2>
}
function ProductsLayout() {
return (
<div>
<h2>Product Management</h2>
<nav>
<NavLink to="list">Product List</NavLink>
<NavLink to="add">Add Item</NavLink>
</nav>
<Outlet />
</div>
)
}
function ProductList() {
return <p>The product list is displayed here...</p>
}
function AddProduct() {
return <p>The form for adding products is displayed here....</p>
}
function Orders() {
return <h2>Order Management</h2>
}
function Settings() {
return <h2>System Settings</h2>
}
function NotFound() {
return <h2>404 — Page Not Found</h2>
}
export default function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<AdminLayout />}>
<Route index element={<Dashboard />} />
<Route path="products" element={<ProductsLayout />}>
<Route index element={<ProductList />} />
<Route path="list" element={<ProductList />} />
<Route path="add" element={<AddProduct />} />
</Route>
<Route path="orders" element={<Orders />} />
<Route path="settings" element={<Settings />} />
<Route path="*" element={<NotFound />} />
</Route>
</Routes>
</BrowserRouter>
)
}
السمات المعمارية: يتألف التطبيق بأكمله من عنصر <BrowserRouter> واحد فقط وعنصر <Routes> واحد من المستوى الأعلى. ويُستخدم AdminLayout كنظام تخطيط شامل يدمج جميع مكونات الصفحة عبر منافذ (outlets). ويُعد نمط «مسار واحد + تخطيط متداخل» هذا البنية القياسية لتطبيقات React متوسطة الحجم.
📖 ملخص
- يعتمد BrowserRouter على واجهة برمجة تطبيقات History API، بينما يعتمد HashRouter على تجزئات عناوين URL؛ ويُوصى باستخدام الأول (يتطلب دعمًا من الخادم)
- يتولى مكون «Routes» عملية المطابقة؛ بينما يحدد مكون «Route» التعيين بين المسارات والمكونات
- الرابط: للتنقل التصريحي؛ يوفر NavLink إمكانية الكشف عن الحالة النشطة (دالة الاستدعاء isActive)
- تستخرج دالة useParams المعلمات الديناميكية من عنوان URL وتدعم المعلمات متعددة الأجزاء (
:id،:catId/:prodId) - تستخدم المسارات المتداخلة «المنافذ» لإعادة استخدام التخطيطات، ويوفر المسار «index» المحتوى الافتراضي للمسار الأصلي
- عند نشر BrowserRouter في بيئة الإنتاج، يجب عليك تكوين قواعد إعادة كتابة عناوين URL الخاصة بالخادم.
- في التوجيه المتداخل، يجب الانتباه إلى الفرق بين المسارات النسبية (التي لا تبدأ بـ
/) والمسارات المطلقة (التي تبدأ بـ/)
📝 تمارين
- قم بإنشاء لوحة تحكم إدارية مكونة من 4 صفحات: الصفحة الرئيسية، وقائمة المستخدمين، وتفاصيل المستخدم (استرجع معرّف المستخدم باستخدام
useParams)، وصفحة «نبذة عنا». استخدمLinkللتنقل، وتأكد من أنNavLinkيحتوي على نمط نشط. - إضافة مسارات متداخلة إلى صفحة قائمة المستخدمين:
/usersتعرض قائمة المستخدمين، و/users/:idتعرض تفاصيل المستخدم؛ وكلاهما يشتركان في مكون تخطيط يحتوي على رأس الصفحة (باستخدام منفذ). - أضف صفحة 404 في نهاية جدول التوجيه لعرض رسالة سهلة الفهم عندما يحاول المستخدم الوصول إلى مسار غير موجود.