Next.js
مسیریاب app
برنامه تان را بسیازید
مسیریابی Routing
مسیرهای موازی

مسیرهای موازی

مسیرهای موازی به شما امکان می دهد تا به طور همزمان یا مشروط، یک یا چند صفحه را در یک چیدمان (layout) واحد رندر کنید. این مسیرها برای بخش های بسیار داینامیک در یک برنامه، مانند داشبوردها و فیدهای شبکه های اجتماعی مفید هستند.

به عنوان مثال، با در نظر گرفتن یک داشبورد، می توانید از مسیرهای موازی برای رندر همزمان صفحات team و analytics استفاده کنید:


نمودار مسیرهای موازینمودار مسیرهای موازی

اسلات‌ها (Slots)

مسیرهای موازی با استفاده از اسلات‌های نامگذاری شده ایجاد می شوند. اسلات ها با قرارداد @folder تعریف می شوند. به عنوان مثال، ساختار فایل زیر دو اسلات را تعریف می کند: @analytics و @team


مسیرهای موازی ساختار فایل سیستممسیرهای موازی ساختار فایل سیستم

اسلات ها به عنوان prop به layout والد مشترک منتقل می شوند. برای مثال بالا، کامپوننت در app/layout.js اکنون prop های @analytics و @team را می پذیرد و می تواند آنها را به طور موازی در کنار prop children رندر کند.

app/layout.tsx
export default function Layout({
	children,
	team,
	analytics,
}: {
	children: React.ReactNode
	analytics: React.ReactNode
	team: React.ReactNode
}) {
	return (
		<>
			{children}
			{team}
			{analytics}
		</>
	)
}

با این حال، اسلات‌ها بخش های مسیر نیستند و بر ساختار URL تأثیر نمی‌گذارند. برای مثال، برای /@analytics/views، URL برابر با /views خواهد بود زیرا @analytics یک اسلات است.

خوب است بدانید:

  • prop children یک اسلات ضمنی است که نیازی به نگاشت به یک پوشه ندارد. این به این معنی است که app/page.js معادل با app/@children/page.js است.

حالت فعال و پیمایش

به طور پیش فرض، Next.js برای هر اسلات، ردیفی از حالت فعال (یا زیرصفحه) نگه می دارد. با این حال، محتوای رندر شده درون یک اسلات به نوع پیمایش بستگی دارد:

  • ناوبری نرم (Soft Navigation): در طول پیمایش سمت کاربر، Next.js یک رندر جزئی انجام می دهد، زیرصفحه درون اسلات را تغییر می دهد، در حالی که زیرصفحات فعال اسلات های دیگر را حتی اگر با URL فعلی مطابقت ندارند، حفظ می کند.
  • ناوبری سخت (Hard Navigation): پس از بارگذاری کامل صفحه (browser refresh)، Next.js نمی تواند حالت فعال را برای اسلات هایی که با URL فعلی مطابقت ندارند، تعیین کند. در عوض، یک فایل default.js را برای اسلات های بدون تطابق رندر می کند، یا اگر default.js وجود ندارد، خطای 404 را نمایش می دهد.

خوب است بدانید:

  • خطای 404 برای مسیرهای بدون تطابق به جلوگیری از رندر تصادفی یک مسیر موازی در صفحه ای که برای آن در نظر گرفته نشده کمک می کند.

default.js

می توانید یک فایل default.js را برای رندر به عنوان یک پشتیبان برای اسلات های بدون تطابق در هنگام بارگذاری اولیه یا بارگذاری مجدد کل صفحه تعریف کنید.

ساختار پوشه زیر را در نظر بگیرید. اسلات @team یک صفحه /settings دارد، اما اسلات @analytics ندارد.


مسیرهای موازی مسیرهای بدون هم‌خوانیمسیرهای موازی مسیرهای بدون هم‌خوانی

هنگام پیمایش به /settings، اسلات @team صفحه /settings را رندر می کند و در عین حال صفحه فعال فعلی را برای اسلات @analytics حفظ می کند.

در زمان رفرش، Next.js یک default.js را برای @analytics رندر خواهد کرد. اگر default.js وجود نداشته باشد، به جای آن خطای 404 نمایش داده می شود.

علاوه بر این، از آنجایی که children یک اسلات ضمنی است، شما همچنین نیاز به ایجاد یک فایل default.js برای رندر یک پشتیبان برای children در زمانی که Next.js نمی تواند حالت فعال صفحه والد را بازیابی کند، دارید.

useSelectedLayoutSegment(s)

هر دو useSelectedLayoutSegment و useSelectedLayoutSegments پارامتر parallelRoutesKey را می پذیرند که به شما امکان می دهد بخش مسیر فعال را در یک اسلات بخوانید.

app/layout.tsx
'use client'
 
import { useSelectedLayoutSegment } from 'next/navigation'
 
export default function Layout({ auth }: { auth: React.ReactNode }) {
	const loginSegment = useSelectedLayoutSegment('auth')
	// ...
}

وقتی کاربر به app/@auth/login (یا /login در نوار URL) پیمایش می کند، loginSegment برابر با رشته "login" خواهد بود.

مثال ها

مسیرهای شرطی

می‌توانید از مسیرهای موازی برای رندر شرطی مسیرها بر اساس شرایط خاص، مانند نقش کاربر استفاده کنید. به عنوان مثال، برای رندر کردن یک صفحه داشبورد متفاوت برای نقش های /admin یا /user:


نمودار مسیرهای مشروطنمودار مسیرهای مشروط
app/dashboard/layout.tsx
import { checkUserRole } from '@/lib/auth'
 
export default function Layout({
	user,
	admin,
}: {
	user: React.ReactNode
	admin: React.ReactNode
}) {
	const role = checkUserRole()
	return <>{role === 'admin' ? admin : user}</>
}

گروه‌های تب

می‌توانید یک layout را در داخل یک اسلات اضافه کنید تا به کاربران اجازه دهید به صورت مستقل در اسلات پیمایش کنند. این برای ایجاد تب ها مفید است.

برای مثال، اسلات @analytics دارای دو زیرصفحه است: /page-views و /visitors.


اسلات تجزیه و تحلیل با دو صفحه فرعی و یک طرح بندیاسلات تجزیه و تحلیل با دو صفحه فرعی و یک طرح بندی

در داخل @analytics، یک فایل layout برای به اشتراک گذاشتن تب ها بین دو صفحه ایجاد کنید:

app/@analytics/layout.tsx
import Link from 'next/link'
 
export default function Layout({ children }: { children: React.ReactNode }) {
	return (
		<>
			<nav>
				<Link href="/page-views">Page Views</Link>
				<Link href="/visitors">Visitors</Link>
			</nav>
			<div>{children}</div>
		</>
	)
}

مودال ها

مسیرهای موازی را می‌توان همراه با رهگیری مسیرها (Intercepting Routes) برای ایجاد مودال‌ها استفاده کرد. این به شما امکان می دهد چالش های رایج هنگام ساخت مودال ها را حل کنید، مانند:

  • امکان اشتراک گذاری محتوای مودال از طریق URL.
  • حفظ زمینه Preserving context زمانی که صفحه تازه سازی می شود، به جای بستن مودال.
  • Closing the modal on backwards navigation rather than going to the previous route.
  • بستن مودال در پیمایش به عقب به جای رفتن به مسیر قبلی.
  • باز کردن مجدد مودال در پیمایش به جلو.

الگوی رابط کاربری زیر را در نظر بگیرید که در آن کاربر می تواند یک مودال ورود را از یک layout با استفاده از پیمایش سمت کاربر باز کند یا به یک صفحه جداگانه /login دسترسی پیدا کند:


نمودار مسیرهای موازینمودار مسیرهای موازی

برای اجرای این الگو، ابتدا با ایجاد مسیری به نام /login شروع کنید که صفحه اصلی ورود شما را رندر می کند.


نمودار مسیرهای موازینمودار مسیرهای موازی
app/login/page.tsx
import { Login } from '@/app/ui/login'
 
export default function Page() {
	return <Login />
}

سپس، داخل اسلات @auth، یک فایل default.js اضافه کنید که null را برمی گرداند. این تضمین می کند که مودال زمانی که فعال نیست، رندر نمی شود.

app/@auth/default.tsx
export default function Default() {
	return null
}

در داخل اسلات @auth، مسیر /login را با به روز رسانی پوشه /(.)login رهگیری کنید. کامپوننت <Modal> و فرزندان آن را به فایل /(.)login/page.tsx وارد کنید:

app/@auth/(.)login/page.tsx
import { Modal } from '@/app/ui/modal'
import { Login } from '@/app/ui/login'
 
export default function Page() {
	return (
		<Modal>
			<Login />
		</Modal>
	)
}

خوب است بدانید:

  • قراردادی که برای رهگیری مسیر intercept the route استفاده می شود، مانند (.)، به ساختار سیستم فایل شما بستگی دارد. به قرارداد رهگیری مسیرها مراجعه کنید.
  • با جدا کردن عملکرد <Modal> از محتوای مودال (<Login>)، می توانید اطمینان حاصل کنید که هر محتوایی داخل مودال، مانند فرم ها، Server Components هستند. برای اطلاعات بیشتر به ترکیب Client و Server Components مراجعه کنید.

باز کردن مودال

حالا می توانید از روتر Next.js برای باز کردن و بستن مودال استفاده کنید. این تضمین می کند که URL زمانی که مودال باز است و هنگام پیمایش به عقب و جلو به درستی به روز شود.

برای باز کردن مودال، اسلات @auth را به عنوان یک پراپ به طرح بندی والد منتقل کرده و آن را در کنار پراپ children رندر کنید.

app/layout.tsx
import Link from 'next/link'
 
export default function Layout({
	auth,
	children,
}: {
	auth: React.ReactNode
	children: React.ReactNode
}) {
	return (
		<>
			<nav>
				<Link href="/login">Open modal</Link>
			</nav>
			<div>{auth}</div>
			<div>{children}</div>
		</>
	)
}

هنگامی که کاربر روی <Link> کلیک می کند، به جای پیمایش به صفحه /login، مودال باز خواهد شد. با این حال، در زمان رفرش یا بارگذاری اولیه، پیمایش به /login کاربر را به صفحه اصلی ورود هدایت می کند.

بستن مودال

می توانید با فراخوانی router.back() یا با استفاده از کامپوننت <Link>، مودال را ببندید.

app/ui/modal.tsx
'use client'
 
import { useRouter } from 'next/navigation'
 
export function Modal({ children }: { children: React.ReactNode }) {
	const router = useRouter()
 
	return (
		<>
			<button
				onClick={() => {
					router.back()
				}}
			>
				Close modal
			</button>
			<div>{children}</div>
		</>
	)
}

هنگام استفاده از کامپوننت <Link> برای پیمایش خارج از صفحه ای که دیگر نباید اسلات @auth را رندر کند، از یک مسیر همه گیر استفاده می کنیم که null را برمی گرداند.

app/ui/modal.tsx
import Link from 'next/link'
 
export function Modal({ children }: { children: React.ReactNode }) {
	return (
		<>
			<Link href="/">Close modal</Link>
			<div>{children}</div>
		</>
	)
}
app/@auth/[...catchAll]/page.tsx
export default function CatchAll() {
	return null
}

خوب است بدانید:

  • ما از یک مسیر همه گیر در اسلات @auth برای بستن مودال به دلیل رفتاری که در حالت فعال و پیمایش توضیح داده شده است استفاده می کنیم. از آنجایی که پیمایش‌های سمت کاربر به مسیری که دیگر با اسلات مطابقت ندارد، همچنان قابل مشاهده خواهند بود، باید اسلات را با مسیری که null را برمی‌گرداند مطابقت دهیم تا مودال بسته شود.
  • نمونه‌های دیگر می‌توانند شامل باز کردن یک مودال عکس در یک گالری در حالی که یک صفحه اختصاصی /photo/[id] نیز وجود داشته باشد، یا باز کردن یک سبد خرید در یک مودال جانبی باشند.
  • نمونه‌ای از استفاده از مسیرهای موازی با رهگیری مسیرها و مودال‌ها را در اینجا مشاهده کنید: مشاهده مثال (opens in a new tab).

رابط کاربری بارگیری و خطا

مسیرهای موازی را می‌توان به طور مستقل استریم کرد و به شما این امکان را می‌دهد که حالت‌های خطا و بارگیری مستقل را برای هر مسیر تعریف کنید:


مسیرهای موازی خطای سفارشی و حالت های بارگیری را فعال می کنندمسیرهای موازی خطای سفارشی و حالت های بارگیری را فعال می کنند

برای اطلاعات بیشتر به مستندات رابط کاربری بارگذاری و هندل کردن خطا مراجعه کنید.