الصفحات والتخطيطات

نوصي بقراءة صفحات أساسيات التوجيه وتحديد المسارات قبل المتابعة.

قدم موجه التطبيق (App Router) في Next.js 13 اصطلاحات ملفات جديدة لإنشاء صفحات، تخطيطات مشتركة، وقوالب بسهولة. سيرشدك هذا الصفحة حول كيفية استخدام هذه الملفات الخاصة في تطبيق Next.js الخاص بك.

الصفحات

الصفحة هي واجهة مستخدم فريدة لمسار معين. يمكنك تعريف الصفحات عن طريق تصدير مكون من ملف page.js. استخدم المجلدات المتداخلة لتحديد مسار وملف page.js لجعل المسار متاحًا للجمهور.

أنشئ صفحتك الأولى عن طريق إضافة ملف page.js داخل مجلد app:

ملف page.js الخاص
// `app/page.tsx` هو واجهة المستخدم لمسار `/`
export default function Page() {
  return <h1>مرحبًا، الصفحة الرئيسية!</h1>
}
// `app/page.js` هو واجهة المستخدم لمسار `/`
export default function Page() {
  return <h1>مرحبًا، الصفحة الرئيسية!</h1>
}
// `app/dashboard/page.tsx` هو واجهة المستخدم لمسار `/dashboard`
export default function Page() {
  return <h1>مرحبًا، صفحة لوحة التحكم!</h1>
}
// `app/dashboard/page.js` هو واجهة المستخدم لمسار `/dashboard`
export default function Page() {
  return <h1>مرحبًا، صفحة لوحة التحكم!</h1>
}

جيد أن تعرف:

التخطيطات

التخطيط هو واجهة مستخدم مشتركة بين عدة صفحات. عند التنقل، تحافظ التخطيطات على الحالة، تبقى تفاعلية، ولا تعيد التصيير. يمكن أيضًا تداخل التخطيطات.

يمكنك تعريف تخطيط عن طريق تصدير مكون React افتراضي من ملف layout.js. يجب أن يقبل المكون خاصية children التي سيتم تعبئتها بتخطيط فرعي (إذا كان موجودًا) أو صفحة فرعية أثناء التصيير.

ملف layout.js الخاص
export default function DashboardLayout({
  children, // سيكون صفحة أو تخطيط متداخل
}: {
  children: React.ReactNode
}) {
  return (
    <section>
      {/* ضع واجهة المستخدم المشتركة هنا مثل رأس أو شريط جانبي */}
      <nav></nav>

      {children}
    </section>
  )
}
export default function DashboardLayout({
  children, // سيكون صفحة أو تخطيط متداخل
}) {
  return (
    <section>
      {/* ضع واجهة المستخدم المشتركة هنا مثل رأس أو شريط جانبي */}
      <nav></nav>

      {children}
    </section>
  )
}

جيد أن تعرف:

  • يسمى التخطيط الأعلى بـ تخطيط الجذر (Root Layout). هذا التخطيط مطلوب ويتم مشاركته عبر جميع الصفحات في التطبيق. يجب أن تحتوي تخطيطات الجذر على وسم html و body.
  • يمكن لأي جزء من المسار اختياريًا تعريف تخطيطه الخاص. ستتم مشاركة هذه التخطيطات عبر جميع الصفحات في هذا الجزء.
  • التخطيطات في المسار متداخلة افتراضيًا. كل تخطيط رئيسي يلف التخطيطات الفرعية تحته باستخدام خاصية React children.
  • يمكنك استخدام مجموعات المسار (Route Groups) لاختيار أجزاء مسار معينة داخل أو خارج التخطيطات المشتركة.
  • التخطيطات هي مكونات خادم (Server Components) افتراضيًا ولكن يمكن تعيينها كمكون عميل (Client Component).
  • يمكن للتخطيطات جلب البيانات. راجع قسم جلب البيانات لمزيد من المعلومات.
  • لا يمكن تمرير البيانات بين التخطيط الرئيسي وأطفاله. ومع ذلك، يمكنك جلب نفس البيانات في مسار أكثر من مرة، وسيقوم React بإزالة التكرارات تلقائيًا دون التأثير على الأداء.
  • لا يمكن للتخطيطات الوصول إلى أجزاء المسار أسفلها. للوصول إلى جميع أجزاء المسار، يمكنك استخدام useSelectedLayoutSegment أو [useSelectedLayoutSegments في مكون عميل.
  • يمكن استخدام امتدادات الملفات .js أو .jsx أو .tsx للتخطيطات.
  • يمكن تعريف ملف layout.js و page.js في نفس المجلد. سيلف التخطيط الصفحة.

تخطيط الجذر (مطلوب)

يتم تعريف تخطيط الجذر في المستوى الأعلى من مجلد app وينطبق على جميع المسارات. يتيح لك هذا التخطيط تعديل HTML الأولي المرسل من الخادم.

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="ar">
      <body>{children}</body>
    </html>
  )
}
export default function RootLayout({ children }) {
  return (
    <html lang="ar">
      <body>{children}</body>
    </html>
  )
}

جيد أن تعرف:

الانتقال من مجلد pages: يحل تخطيط الجذر محل ملفات _app.js و _document.js. راجع دليل الانتقال.

تداخل التخطيطات

تنطبق التخطيطات المعرفة داخل مجلد (مثل app/dashboard/layout.js) على أجزاء مسار معينة (مثل acme.com/dashboard) وتصدر عندما تكون هذه الأجزاء نشطة. افتراضيًا، التخطيطات في تسلسل الملفات متداخلة، مما يعني أنها تغلف التخطيطات الفرعية عبر خاصية children.

تخطيط متداخل
export default function DashboardLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return <section>{children}</section>
}
export default function DashboardLayout({ children }) {
  return <section>{children}</section>
}

جيد أن تعرف:

  • فقط تخطيط الجذر يمكن أن يحتوي على وسم <html> و <body>.

إذا قمت بدمج التخطيطين أعلاه، فسيلف تخطيط الجذر (app/layout.js) تخطيط لوحة التحكم (app/dashboard/layout.js)، والذي بدوره سيلف أجزاء المسار داخل app/dashboard/*.

سيتم تداخل التخطيطين كما يلي:

تخطيطات متداخلة

يمكنك استخدام مجموعات المسار (Route Groups) لاختيار أجزاء مسار معينة داخل أو خارج التخطيطات المشتركة.

القوالب

القوالب مشابهة للتخطيطات من حيث أنها تغلف كل تخطيط فرعي أو صفحة. على عكس التخطيطات التي تبقى عبر المسارات وتحافظ على الحالة، تنشئ القوالب نسخة جديدة لكل طفل لها عند التنقل. هذا يعني أنه عندما ينتقل المستخدم بين المسارات التي تشترك في قالب، يتم تحميل نسخة جديدة من المكون، يتم إعادة إنشاء عناصر DOM، لا يتم الحفاظ على الحالة، ويتم إعادة مزامنة التأثيرات.

قد تكون هناك حالات تحتاج فيها إلى هذه السلوكيات المحددة، وتكون القوالب خيارًا أكثر ملاءمة من التخطيطات. على سبيل المثال:

  • ميزات تعتمد على useEffect (مثل تسجيل مشاهدات الصفحة) و useState (مثل نموذج تعليقات لكل صفحة).
  • لتغيير سلوك الإطار الافتراضي. على سبيل المثال، حدود Suspense داخل التخطيطات تظهر الحالة الاحتياطية فقط عند تحميل التخطيط لأول مرة وليس عند تبديل الصفحات. بالنسبة للقوالب، تظهر الحالة الاحتياطية في كل تنقل.

يمكن تعريف قالب عن طريق تصدير مكون React افتراضي من ملف template.js. يجب أن يقبل المكون خاصية children.

ملف template.js الخاص
export default function Template({ children }: { children: React.ReactNode }) {
  return <div>{children}</div>
}
export default function Template({ children }) {
  return <div>{children}</div>
}

من حيث التداخل، يتم تصيير template.js بين التخطيط وأطفاله. إليك ناتج مبسط:

الناتج
<Layout>
  {/* لاحظ أن القالب يحصل على مفتاح فريد. */}
  <Template key={routeParam}>{children}</Template>
</Layout>

تعديل <head>

في مجلد app، يمكنك تعديل عناصر HTML <head> مثل title و meta باستخدام دعم SEO المدمج.

يمكن تعريف البيانات الوصفية عن طريق تصدير كائن metadata أو دالة generateMetadata في ملف layout.js أو page.js.

import { Metadata } from 'next'

export const metadata: Metadata = {
  title: 'Next.js',
}

export default function Page() {
  return '...'
}
export const metadata = {
  title: 'Next.js',
}

export default function Page() {
  return '...'
}

جيد أن تعرف: يجب ألا تضيف يدويًا وسوم <head> مثل <title> و <meta> إلى تخطيطات الجذر. بدلاً من ذلك، يجب استخدام واجهة برمجة تطبيقات البيانات الوصفية (Metadata API) التي تتعامل تلقائيًا مع المتطلبات المتقدمة مثل البث وإزالة التكرارات من عناصر <head>.

تعرف على المزيد حول خيارات البيانات الوصفية المتاحة في مرجع API.