إتقان Next.js أنماط موجّه التطبيق لتطبيقات الويب القابلة للتوسع
١٨ ديسمبر ٢٠٢٥
باختصار
- يقدم Next.js App Router نموذجًا جديدًا للتفكير في التوجيه، التخطيطات، واسترجاع البيانات باستخدام React مكونات الخادم (RSCs)1.
- فهم أنماط مثل التخطيطات المتداخلة، المسارات المتوازية، وحالات التحميل هو مفتاح بناء تطبيقات قابلة للتوسع وسهلة الصيانة.
- الحدود بين الخادم والعميل تحدد خصائص الأداء والأمان — استخدمها بحكمة.
- الأخطاء الشائعة تشمل جلب البيانات بشكل مفرط، التخزين المؤقت غير الصحيح، واستخدام غير صحيح للمسارات الديناميكية.
- بهيكلة صحيحة، يمكّن App Router قابلية التوسع في الإنتاج، المراقبة، وسرعة المطورين.
ما ستتعلمه
- كيف يختلف App Router عن Pages Router القديم.
- أنماط البنية الأساسية (التخطيطات، المسارات المتداخلة، المسارات المتوازية، المسارات المُعترضة).
- أنماط جلب البيانات باستخدام مكونات الخادم واستراتيجيات التخزين المؤقت لـ
fetch(). - معالجة الأخطاء، حالات التحميل، وحدود الترقب.
- أفضل الممارسات للأمان والأداء.
- كيفية هيكلة تطبيق Next.js الخاص بك للصيانة طويلة الأمد.
المتطلبات الأساسية
يجب أن تكون مرتاحًا مع:
- أساسيات React (الHooks، المكونات، الخصائص).
- مفاهيم أساسية لـ Next.js (الصفحات، التوجيه، مسارات API).
- الاطلاع على Node.js وnpm/yarn.
إذا كنت قد بنيت مشروع Next.js قبل الإصدار 13، فهذه المقالة ستساعدك على ربط الأنماط القديمة بالجديدة ذهنيًا.
المقدمة: من الصفحات إلى التطبيقات
Next.js App Router — الذي تم تقديمه في الإصدار 13 واستقر في 142 — يمثل تحولًا جذريًا. بدلاً من التوجيه القائم على الملفات تحت /pages, يستخدم App Router دليلًا جديدًا /app مع React مكونات الخادم (RSCs) في قلبه.
يسمح هذا النموذج الجديد بالالبث، التصيير الجزئي، واسترجاع البيانات الموجهة من الخادم — مع الحفاظ على نموذج الواجهة الإعلانية لـ React. إنه ليس مجرد هيكل مجلد جديد؛ بل هو طريقة جديدة للتفكير في تطبيقات React.
دعونا نتصور هذا التحول:
| الميزة | Pages Router | App Router |
|---|---|---|
| التصيير | العميل + SSR | مكونات الخادم + البث |
| استرجاع البيانات | getStaticProps, getServerSideProps |
fetch() في مكونات الخادم |
| التخطيطات | تخصيص _app.js و _document.js |
التخطيطات المتداخلة layout.js لكل مسار |
| التوجيه | قائم على الملفات تحت /pages |
قائم على الملفات تحت /app مع أقسام |
| معالجة الأخطاء | تخصيص _error.js |
error.js لكل قسم مسار |
| حالات التحميل | يدوي | loading.js لكل قسم مسار |
العناصر الأساسية
1. التخطيطات
كل مجلد في الدليل /app يمكنه تحديد ملف layout.js يحيط بجميع المسارات الفرعية. التخطيطات تكون دائمة، مما يعني أنها لا تُعاد تحميلها بين التنقلات — فائدة أداء كبيرة.
// app/layout.js
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>
<Navbar />
<main>{children}</main>
<Footer />
</body>
</html>
);
}
يمكنك ترتيب التخطيطات المتداخلة لأقسام موقعك — على سبيل المثال، تخطيط لوحة القيادة مختلف عن موقع التسويق.
// app/dashboard/layout.js
export default function DashboardLayout({ children }) {
return (
<section className="dashboard">
<Sidebar />
<div className="content">{children}</div>
</section>
);
}
2. الصفحات وأقسام المسارات
كل مجلد داخل /app يمثل قسم مسار. ملف page.js يحدد واجهة المستخدم لذلك المسار.
// app/dashboard/page.js
export default function DashboardPage() {
return <h1>Welcome to your dashboard</h1>;
}
تستخدم المسارات الديناميكية الأقواس المربعة، مثلما كان من قبل:
// app/posts/[slug]/page.js
export default async function PostPage({ params }) {
const post = await fetch(`https://API.example.com/posts/${params.slug}`).then(res => res.json());
return <article>{post.title}</article>;
}
أنماط جلب البيانات
جلب البيانات أصبح الآن مدمجًا مع المكونات. يمكنك استدعاء fetch() مباشرة داخل مكون الخادم — لا حاجة لـ getServerSideProps.
جلب البيانات الثابت (مُخزن مؤقتًا افتراضيًا)
// app/blog/page.js
export default async function BlogPage() {
const posts = await fetch('https://API.example.com/posts', {
next: { revalidate: 60 }, // ISR replacement
}).then(res => res.json());
return (
<ul>
{posts.map(post => <li key={post.id}>{post.title}</li>)}
</ul>
);
}
- الخيار
next.revalidateيستبدل إعادة التوليد الثابت التدريجي (ISR). - يتم تخزين البيانات مؤقتًا وإعادة التحقق منها تلقائيًا.
جلب البيانات الديناميكي (بدون تخزين مؤقت)
export default async function DashboardStats() {
const stats = await fetch('https://API.example.com/stats', { cache: 'no-store' }).then(res => res.json());
return <StatsWidget data={stats} />;
}
هذا النمط يُستخدم للداشبورد أو للبيانات الخاصة بالمستخدم.
الجلب المتوازي للبيانات
في مكونات الخادم، يمكنك جلب موارد متعددة بشكل متزامن:
export default async function Home() {
const [posts, users] = await Promise.all([
fetch('https://API.example.com/posts').then(res => res.json()),
fetch('https://API.example.com/users').then(res => res.json()),
]);
return (
<>
<PostsList posts={posts} />
<UsersList users={users} />
</>
);
}
الجلب المتوازي عادةً يقلل زمن الانتظار في السيناريوهات المحدودة بـ I/O3.
أنماط التوجيه المتقدمة
التخطيطات المتداخلة
التخطيطات المتداخلة تسمح لأقسام مختلفة من التطبيق بالاحتفاظ بهيكلها الخاص.
app/
├─ layout.js
├─ dashboard/
│ ├─ layout.js
│ ├─ settings/
│ │ └─ page.js
│ └─ analytics/
│ └─ page.js
التنقل بين /dashboard/settings و /dashboard/analytics يحافظ على الشريط الجانبي والشريط العلوي المحدد في dashboard/layout.js.
المسارات المتوازية
المسارات المتوازية تسمح بعرض عدة عروض مستقلة في نفس الوقت.
app/
├─ layout.js
├─ @feed/
│ └─ page.js
├─ @activity/
│ └─ page.js
└─ page.js
المسارات المُقاطعة
المسارات المُقاطعة تسمح لك بعرض نوافذ منبثقة دون مغادرة الصفحة الحالية — مثالية للمعاينات أو تدفقات المصادقة.
app/
├─ feed/
│ ├─ page.js
│ └─ (.)modal/
│ └─ login/page.js
متى تستخدم مقابل متى لا تستخدم App Router
| حالة الاستخدام | استخدم App Router | استمر مع Pages Router |
|---|---|---|
| المشاريع الجديدة | ✅ | ❌ |
| نقل تطبيق كبير موجود | ⚠️ Gradually | ✅ |
| محتوى SSR ثقيل أو ثابت | ✅ | ✅ |
| تحتاج إلى React Server Components | ✅ | ❌ |
| استخدام إضافات Next.js القديمة | ❌ | ✅ |
مثال من الواقع: هندسة لوحة تحكم قابلة للتوسع
تخيل لوحة تحكم SaaS للتحليلات. كل قسم — المؤشرات، التقارير، الإعدادات — يستخدم تخطيطات متداخلة ومكونات Server Components للبث.
graph TD
A[Root Layout] --> B[Dashboard Layout]
B --> C[Metrics Page]
B --> D[Reports Page]
B --> E[Settings Page]
تتيح هذه البنية التصيير الجزئي وواجهة تنقل مستمرة. تستخدم الخدمات الكبيرة هذا النموذج للأداء والصيانة4.
آثار الأداء
App Router يستفيد من React Server Components التي ترسل أشجار مكونات مسلسلة بدلاً من HTML الكامل. هذا يقلل حجم حزمة العميل ويحسن وقت التفاعل (TTI)5.
نصائح أداء رئيسية
- نقل جلب البيانات إلى الخادم كلما أمكن.
- استخدم حدود Suspense لبث المحتوى الجزئي.
- اخزن مؤقتًا بذكاء — استخدم
revalidateللبيانات شبه الثابتة. - تجنب مكونات العميل ما لم تكن التفاعلية مطلوبة.
مثال للبث باستخدام Suspense:
import { Suspense } from 'React';
import PostsList from './PostsList';
export default function Page() {
return (
<Suspense fallback={<p>Loading posts...</p>}>
<PostsList />
</Suspense>
);
}
اعتبارات الأمان
- Server Components تعمل على الخادم، مما يخفي المنطق الحساس عن العملاء.
- تجنب كشف الأسرار في مكونات العميل.
- استخدم متغيرات البيئة بأمان (عبر
process.envفي Server Components). - اتبع إرشادات OWASP للتحقق من المدخلات ومنع XSS6.
رؤى حول القابلية للتوسع
البنية المودولارية لموجّه App Router تتيح التوسع المستقل لأجزاء المسارات. مع دمج التصيير على الحافة وإعادة التحقق الثابت، يدعم السيناريوهات ذات حركة المرور العالية.
تجمع الخدمات الكبيرة غالبًا:
- وظائف الحافة للردود منخفضة التأخير.
- إعادة التحقق الثابت للأقسام الثقيلة بالمحتوى.
- التصيير الديناميكي للوحات تحكم مخصصة.
استراتيجيات الاختبار
- الاختبار الوحدوي: استخدم Jest أو Vitest للمكونات المعزولة.
- الاختبار التكاملي: استخدم Playwright أو Cypress لانتقالات المسارات.
- اختبار مكونات Server Components: قم بمحاكاة استجابات
fetch()باستخدام MSW (Mock Service Worker).
مثال اختبار:
import { render, screen } from '@testing-library/React';
import DashboardPage from '../app/dashboard/page';
test('renders dashboard heading', async () => {
render(<DashboardPage />);
expect(await screen.findByText(/dashboard/i)).toBeInTheDocument();
});
Error Handling Patterns
يمكن لكل مسار تحديد ملف error.js الخاص به:
'use client';
export default function Error({ error, reset }) {
return (
<div>
<h2>Something went wrong!</h2>
<button onClick={() => reset()}>Try again</button>
</div>
);
}
يقوم هذا بعزل الأخطاء في أقسام مسار محددة.
Monitoring and Observability
استخدم سجلات الخادم والتتبع لمراقبة الأداء:
- Next.js مسجل مدمج لتتبع الطلبات.
- OpenTelemetry تكامل للتتبع الموزع7.
- Vercel Analytics أو لوحات تحكم مخصصة لمقاييس مستوى المسار.
مثال لإخراج الطرفية:
$ next build
✔ Compiled successfully
✔ Collected 12 routes (3 static, 9 dynamic)
✔ Server Components optimized
Common Pitfalls & Solutions
| التحديات | السبب | الحل |
|---|---|---|
| الحصول على بيانات زائدة | الجلب في العميل والخادم معاً | دمج الجلب في مكونات الخادم |
| أخطاء الترطيب | خلط حالة الخادم/العميل بشكل خاطئ | استخدم use client فقط عند الحاجة |
| عمليات بناء بطيئة | مسارات ديناميكية مفرطة | استخدم generateStaticParams() للبناء المسبق |
| مشكلات التخزين المؤقت | غياب تكوين revalidate أو cache |
حدد استراتيجية التخزين المؤقت بشكل صريح |
Common Mistakes Everyone Makes
- وضع جميع المكونات كمكونات عميل.
- نسيان تعريف
loading.jsللمسارات غير المتزامنة. - استخدام
useEffectللحصول على بيانات من جانب الخادم. - تجاهل حدود Suspense.
- تعقيد التخطيطات.
Troubleshooting Guide
الخطأ: Dynamic server usage: fetch() called in Client Component
- ✅ نقل
fetch()إلى مكون خادم.
الخطأ: Hydration failed because the initial UI does not match
- ✅ تأكد من تطابق العرض بين الخادم والعميل.
الخطأ: Cannot read property 'params' of undefined
- ✅ تحقق من تسمية أقسام المسار وتأكد من تفكيك
paramsبشكل صحيح.
Key Takeaways
Next.js App Router ليس مجرد API جديد — بل هو بنية جديدة.
- استخدم مكونات الخادم للأداء والأمان.
- استخدم التخطيطات المتداخلة وSuspense لواجهات المستخدم القابلة للتركيب.
- خزن مؤقتًا بذكاء واختبر بشكل مفصل.
- ابدأ صغيرًا وقم بالتحويل تدريجيًا.
الأسئلة الشائعة
س1: هل يمكنني مزج App Router و Pages Router؟
نعم، يمكنهم التعايش. هذا مفيد للهجرة التدريجية2.
س2: هل أحتاج إلى إعادة كتابة مسارات API؟
لا.
/pages/APIالمسارات لا تزال تعمل مع App Router.
س3: هل مكونات العميل أبطأ؟
ليس بشكل متأصل، لكنها تزيد حجم الحزمة. استخدمها فقط للتفاعلية.
س4: هل يدعم App Router الوسيط؟
نعم، الوسيط لا يزال يعمل قبل حل المسار2.
س5: كيف أقوم بنشر تطبيقات App Router؟
انشر كالمعتاد — مدعوم بشكل طبيعي من Vercel ومضيفات Node.js الأخرى.
الخطوات التالية / القراءة الإضافية
هوامش
-
React مكونات الخادم RFC – React وثائق (React.dev) ↩
-
MDN Web Docs – Promise.all() والتوازي (developer.mozilla.org) ↩
-
React مؤتمر 2023 – مكونات الخادم وهندسة التدفق (React.dev/blog) ↩
-
Vercel Documentation – الأداء والعرض على الحافة (vercel.com/docs) ↩
-
OWASP أعلى 10 مخاطر أمنية (owasp.org) ↩
-
OpenTelemetry JS SDK – المستندات الرسمية (opentelemetry.io) ↩