Route Groups و سازماندهی مسیرها
تا اینجا هر پوشه در app یک بخش از آدرس را میساخت. اما با بزرگشدن پروژه، نیاز پیدا میکنیم صفحات را بر اساس نقش دستهبندی کنیم، نه بر اساس آدرس: مثلاً صفحات عمومی سایت، صفحات ورود و داشبورد مدیریت. در عین حال نمیخواهیم نام این دستهها در آدرسها ظاهر شود. Route Groups دقیقاً برای همین کار طراحی شدهاند.
پیشنیاز
- درس «Nested Layoutها»
- درس «Root Layout، تنظیم lang="fa" و dir="rtl"»
Route Group چیست؟
اگر نام یک پوشه را داخل پرانتز بنویسید، مثل (public)، آن پوشه یک Route Group میشود. قاعدهی کار:
- نام داخل پرانتز در آدرس نمیآید؛ فقط برای سازماندهی فایلهاست.
- هر گروه میتواند
layout.tsxمخصوص خودش را داشته باشد و این Layout فقط روی مسیرهای داخل همان گروه اعمال میشود. - داخل گروه، پوشهها و
page.tsxها مثل همیشه مسیر میسازند.
| مسیر فایل | آدرس در مرورگر |
|---|---|
app/(public)/about/page.tsx |
/about |
app/(public)/page.tsx |
/ |
app/(dashboard)/dashboard/page.tsx |
/dashboard |
توجه کنید که کلمهی public و اولین dashboard داخل پرانتز در آدرس نیامدهاند. در مثال سوم، dashboard بدون پرانتز یک پوشهی معمولی است که بخش /dashboard را میسازد.
سه کاربرد اصلی Route Groups
- سازماندهی: فایلهای مربوط به یک ناحیهی سایت کنار هم قرار میگیرند بدون اینکه آدرسها تغییر کنند.
- Layout جداگانه برای بخشی از مسیرها: مثلاً صفحات عمومی هدر و فوتر دارند، ولی داشبورد منوی کناری.
- چند Root Layout: میتوان هر گروه را یک Root Layout مستقل با
htmlوbodyخودش داد. این روش کاربرد ویژه دارد و پایینتر کوتاه به آن میپردازیم.
عملی: بازسازی ساختار DevNotes با Route Groups
در درس «Nested Layoutها» هدر و فوتر را داخل Root Layout گذاشتیم تا ایده را ببینیم. اما داشبورد مدیریت قرار نیست همان هدر و فوتر عمومی را داشته باشد. پس ساختار را به این شکل اصلاح میکنیم:
- Root Layout فقط تگهای
htmlوbodyو تنظیمات سراسری را نگه میدارد. - هدر و فوتر به Layout گروه
(public)منتقل میشوند. - داشبورد در گروه
(dashboard)با Layout خودش ساخته میشود.
فایلهای متأثر: app/layout.tsx (سادهتر میشود)، و فایلهای page.tsx صفحهی اصلی، about و posts که به داخل (public) منتقل میشوند. آدرسهای سایت تغییری نمیکنند.
گام ۱: ساخت پوشهها و انتقال فایلها
پوشهی app/(public) را بسازید و این فایلها و پوشهها را به داخل آن منتقل کنید:
app/page.tsx←app/(public)/page.tsxapp/about←app/(public)/aboutapp/posts←app/(public)/posts
فایلهای app/layout.tsx، app/globals.css و app/favicon.ico در جای خود میمانند. در ویرایشگر VS Code میتوانید فایلها را با کشیدن و رها کردن جابهجا کنید.
گام ۲: سادهکردن Root Layout
// app/layout.tsx
import type { Metadata } from "next";
import type { ReactNode } from "react";
import "./globals.css";
export const metadata: Metadata = {
title: "DevNotes",
description: "وبسایتی برای انتشار و مدیریت نوشتههای فنی",
};
export default function RootLayout({
children,
}: Readonly<{ children: ReactNode }>) {
return (
<html lang="fa" dir="rtl">
<body>{children}</body>
</html>
);
}گام ۳: Layout گروه عمومی
// app/(public)/layout.tsx
import type { ReactNode } from "react";
export default function PublicLayout({
children,
}: Readonly<{ children: ReactNode }>) {
return (
<>
<header>
<strong>DevNotes</strong>
</header>
{children}
<footer>
<small>پروژهی آموزشی دورهی Next.js 16</small>
</footer>
</>
);
}گام ۴: Layout و صفحهی گروه داشبورد
// app/(dashboard)/layout.tsx
import type { ReactNode } from "react";
export default function DashboardLayout({
children,
}: Readonly<{ children: ReactNode }>) {
return (
<div>
<aside>
<strong>داشبورد DevNotes</strong>
<nav>
<p>نوشتههای من</p>
<p>تنظیمات</p>
</nav>
</aside>
{children}
</div>
);
}// app/(dashboard)/dashboard/page.tsx
export default function DashboardPage() {
return (
<main>
<h1>داشبورد مدیریت</h1>
<p>مدیریت نوشتهها در فصلهای بعد به این بخش اضافه میشود.</p>
</main>
);
}ساختار نهایی پس از این درس
app/
├── layout.tsx ← Root Layout (فقط html و body)
├── globals.css
├── favicon.ico
├── (public)/
│ ├── layout.tsx ← هدر و فوتر عمومی
│ ├── page.tsx ← /
│ ├── about/
│ │ └── page.tsx ← /about
│ └── posts/
│ ├── layout.tsx ← Layout بخش نوشتهها
│ ├── page.tsx ← /posts
│ └── new/
│ └── page.tsx ← /posts/new
└── (dashboard)/
├── layout.tsx ← منوی کناری داشبورد
└── dashboard/
└── page.tsx ← /dashboardتوضیح کد
app/layout.tsxاکنون فقط ساختار سندِ HTML را تعریف میکند. هر چیزی که فقط به بخشی از سایت مربوط است از آن خارج شده است.<>...</>یک Fragment است؛ چونPublicLayoutمیخواهد هدر،childrenو فوتر را بدون تگ اضافی برگرداند.- هیچکدام از دو Layoutی که در گروهها ساختیم تگ
htmlیاbodyندارند؛ آنها همچنان از Root Layout مشترک میآیند. - آدرسها بدون تغییر ماندهاند:
/،/about،/postsو/posts/newمثل قبل کار میکنند و آدرس جدید/dashboardهم اضافه شده است.
رفتار مورد انتظار
| آدرس | Layoutها (از بیرون به درون) | آنچه میبینید |
|---|---|---|
/ |
Root ← (public) |
هدر، محتوای صفحه، فوتر |
/posts |
Root ← (public) ← posts |
هدر، خط «بخش نوشتهها»، محتوا، فوتر |
/dashboard |
Root ← (dashboard) |
منوی کناری و «داشبورد مدیریت» بدون هدر و فوتر عمومی |
اگر فایلها را جابهجا کردهاید و صفحهای ۴۰۴ داد، یکبار سرور توسعه را با Ctrl + C متوقف و دوباره با npm run dev اجرا کنید.
قاعدهها و محدودیتها
-
تداخل مسیر: دو گروه مختلف نمیتوانند یک آدرس نهایی یکسان بسازند. مثلاً
app/(public)/about/page.tsxوapp/(dashboard)/about/page.tsxهر دو به/aboutمیرسند و Next.js خطا میدهد. -
گروه بدون Layout: داشتن
layout.tsxدر گروه الزامی نیست. اگر نیازی نیست، میتوان گروه را فقط برای مرتبکردن فایلها ساخت. - گروهها قابل تودرتو شدن هستند و میتوان داخل یک گروه، گروه دیگری ساخت، ولی بیشازحد تودرتوکردن فهم پروژه را سخت میکند.
-
گروه جای کنترل دسترسی نیست. قرار دادن صفحه در
(dashboard)آن را محافظت نمیکند و/dashboardفعلاً برای همه باز است. محافظت واقعی را در فصل «Authentication و Security» و «Proxy و کنترل دسترسی» میسازیم. -
جایگاه صفحهی ایجاد نوشته:
/posts/newفعلاً در بخش عمومی مانده است. محل نهایی آن را در درس «ساخت اسکلت اولیهی DevNotes» و فصلهای بعد، همراه با احراز هویت، دوباره بررسی میکنیم.
چند Root Layout در یک پروژه
میتوان فایل app/layout.tsx را نداشت و در عوض در هر گروه یک Root Layout کامل با html و body ساخت. این کار وقتی مفید است که بخشهای سایت اساساً سند HTML متفاوتی دارند، مثلاً زبان یا جهت متفاوت. دو نکتهی مهم:
- اگر
app/layout.tsxنداشته باشید، صفحهی اصلی (/) باید داخل یکی از گروهها تعریف شود. - ناوبری بین دو Root Layout متفاوت، بارگذاری کامل صفحه (Full Page Load) انجام میدهد، نه ناوبری سریع سمت کلاینت.
DevNotes فقط یک زبان و یک جهت دارد، پس فقط یک Root Layout نگه میداریم.
جمعبندی
- Route Group پوشهای با نام داخل پرانتز است که در آدرس ظاهر نمیشود و برای سازماندهی مسیرها و جداکردن Layoutها به کار میرود.
- هر گروه میتواند
layout.tsxخودش را داشته باشد که فقط روی مسیرهای داخل همان گروه اعمال میشود. - دو گروه نباید آدرس نهایی یکسان بسازند.
- قرار دادن صفحه در یک گروه آن را امن نمیکند؛ کنترل دسترسی جداگانه لازم است.
- در DevNotes اکنون گروههای
(public)و(dashboard)داریم و Root Layout فقط ساختار HTML را نگه میدارد. - در درس بعد، «پوشههای خصوصی و ساختاردهی فایلها»، یاد میگیریم فایلهای کمکی را کنار صفحات بگذاریم بدون اینکه مسیر عمومی شوند.
