المسارات المتوازية (Parallel Routes)

تتيح لك المسارات المتوازية (Parallel Routing) عرض صفحة واحدة أو أكثر في نفس التخطيط بشكل متزامن أو مشروط. للأجزاء الديناميكية للغاية في التطبيق، مثل لوحات التحكم والتغذيات في المواقع الاجتماعية، يمكن استخدام المسارات المتوازية لتنفيذ أنماط توجيه معقدة.

على سبيل المثال، يمكنك عرض صفحة الفريق وصفحة التحليلات في نفس الوقت.

مخطط المسارات المتوازية

تسمح المسارات المتوازية بتعريف حالات خطأ وتحميل مستقلة لكل مسار حيث يتم بثها بشكل مستقل.

تمكن المسارات المتوازية حالات خطأ وتحميل مخصصة

تتيح المسارات المتوازية أيضًا عرض فتحة (slot) بشكل مشروط بناءً على شروط معينة، مثل حالة المصادقة. هذا يمكّن من فصل التعليمات البرمجية بالكامل على نفس الرابط.

مخطط المسارات المشروطة

الاصطلاح

يتم إنشاء المسارات المتوازية باستخدام فتحات (slots) مسماة. يتم تعريف الفتحات باستخدام اصطلاح @folder، ويتم تمريرها إلى التخطيط بنفس المستوى كخصائص (props).

الفتحات ليست أجزاء من المسار (route segments) ولا تؤثر على بنية الرابط. مسار الملف /@team/members سيكون متاحًا على /members.

على سبيل المثال، بنية الملف التالية تعرّف فتحتين صريحتين: @analytics و @team.

بنية نظام الملفات للمسارات المتوازية

هيكل المجلد أعلاه يعني أن المكون في app/layout.js يقبل الآن خصائص الفتحات @analytics و @team، ويمكن عرضها بالتوازي مع خاصية children:

export default function Layout(props: {
  children: React.ReactNode
  analytics: React.ReactNode
  team: React.ReactNode
}) {
  return (
    <>
      {props.children}
      {props.team}
      {props.analytics}
    </>
  )
}
export default function Layout(props) {
  return (
    <>
      {props.children}
      {props.team}
      {props.analytics}
    </>
  )
}

جيد للمعرفة: خاصية children هي فتحة ضمنية لا تحتاج إلى تعيين لمجلد. هذا يعني أن app/page.js تعادل app/@children/page.js.

المسارات غير المتطابقة

بشكل افتراضي، المحتوى المعروض داخل فتحة سيتطابق مع الرابط الحالي.

في حالة عدم تطابق فتحة، المحتوى الذي يعرضه Next.js يختلف بناءً على تقنية التوجيه وبنية المجلد.

default.js

يمكنك تعريف ملف default.js ليتم عرضه كحل بديل عندما لا يتمكن Next.js من استعادة الحالة النشطة للفتحة بناءً على الرابط الحالي.

خذ بعين الاعتبار بنية المجلد التالية. فتحة @team لديها مجلد settings، لكن @analytics لا تملكه.

المسارات المتوازية غير المتطابقة

التنقل

عند التنقل، سيعرض Next.js الحالة النشطة السابقة للفتحة، حتى لو لم تتطابق مع الرابط الحالي.

إعادة التحميل

عند إعادة التحميل، سيحاول Next.js أولاً عرض ملف default.js للفتحة غير المتطابقة. إذا لم يكن متاحًا، سيتم عرض خطأ 404.

خطأ 404 للمسارات غير المتطابقة يساعد على ضمان عدم عرض مسار لا ينبغي عرضه بالتوازي.

useSelectedLayoutSegment(s)

كل من useSelectedLayoutSegment و useSelectedLayoutSegments يقبلان parallelRoutesKey، مما يسمح لك بقراءة جزء المسار النشط داخل تلك الفتحة.

'use client'

import { useSelectedLayoutSegment } from 'next/navigation'

export default async function Layout(props: {
  //...
  auth: React.ReactNode
}) {
  const loginSegments = useSelectedLayoutSegment('auth')
  // ...
}
'use client'

import { useSelectedLayoutSegment } from 'next/navigation'

export default async function Layout(props) {
  const loginSegments = useSelectedLayoutSegment('auth')
  // ...
}

عندما ينتقل المستخدم إلى @auth/login، أو /login في شريط العناوين، loginSegments ستكون مساوية للسلسلة "login".

أمثلة

النوافذ المنبثقة (Modals)

يمكن استخدام المسارات المتوازية لعرض النوافذ المنبثقة.

مخطط المسارات المتوازية

فتحة @auth تعرض مكون <Modal> يمكن إظهاره بالانتقال إلى مسار مطابق، مثل /login.

export default async function Layout(props: {
  // ...
  auth: React.ReactNode
}) {
  return (
    <>
      {/* ... */}
      {props.auth}
    </>
  )
}
export default async function Layout(props) {
  return (
    <>
      {/* ... */}
      {props.auth}
    </>
  )
}
import { Modal } from 'components/modal'

export default function Login() {
  return (
    <Modal>
      <h1>تسجيل الدخول</h1>
      {/* ... */}
    </Modal>
  )
}
import { Modal } from 'components/modal'

export default function Login() {
  return (
    <Modal>
      <h1>تسجيل الدخول</h1>
      {/* ... */}
    </Modal>
  )
}

لضمان عدم عرض محتويات النافذة المنبثقة عندما لا تكون نشطة، يمكنك إنشاء ملف default.js يعيد null.

export default function Default() {
  return null
}
export default function Default() {
  return null
}

إغلاق نافذة منبثقة

إذا تم بدء النافذة المنبثقة من خلال التنقل من العميل، مثل استخدام <Link href="/login">، يمكنك إغلاق النافذة المنبثقة عن طريق استدعاء router.back() أو باستخدام مكون Link.

'use client'
import { useRouter } from 'next/navigation'
import { Modal } from 'components/modal'

export default async function Login() {
  const router = useRouter()
  return (
    <Modal>
      <span onClick={() => router.back()}>إغلاق النافذة</span>
      <h1>تسجيل الدخول</h1>
      ...
    </Modal>
  )
}
'use client'
import { useRouter } from 'next/navigation'
import { Modal } from 'components/modal'

export default async function Login() {
  const router = useRouter()
  return (
    <Modal>
      <span onClick={() => router.back()}>إغلاق النافذة</span>
      <h1>تسجيل الدخول</h1>
      ...
    </Modal>
  )
}

المزيد من المعلومات حول النوافذ المنبثقة موجودة في قسم المسارات الاعتراضية (Intercepting Routes).

إذا كنت تريد الانتقال إلى مكان آخر وإغلاق نافذة منبثقة، يمكنك أيضًا استخدام مسار شامل (catch-all route).

مخطط المسارات المتوازية
export default function CatchAll() {
  return null
}
export default function CatchAll() {
  return null
}

المسارات الشاملة لها أولوية على default.js.

المسارات المشروطة

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

import { getUser } from '@/lib/auth'

export default function Layout({
  dashboard,
  login,
}: {
  dashboard: React.ReactNode
  login: React.ReactNode
}) {
  const isLoggedIn = getUser()
  return isLoggedIn ? dashboard : login
}
import { getUser } from '@/lib/auth'

export default function Layout({ dashboard, login }) {
  const isLoggedIn = getUser()
  return isLoggedIn ? dashboard : login
}
مثال على المسارات المشروطة للمصادقة