ساخت اسکلت اولیهی DevNotes
در این فصل با صفحهها، Layoutها، Route Groups، پوشههای خصوصی و فایلهای ویژه آشنا شدیم. حالا همهی آنها را کنار هم میگذاریم و اسکلت کامل DevNotes را میسازیم: مسیرهای عمومی، صفحات ورود و ثبتنام و بخش داشبورد. هنوز دادهی واقعی، فرم و استایل نداریم؛ هدف این است که ساختار پروژه از همین حالا درست و ثابت باشد تا در فصلهای بعد فقط محتوا و منطق را به آن اضافه کنیم.
پیشنیاز
- تمام درسهای فصل دوم تا «فایلهای ویژهی App Router»
- پروژهی
devnotesبا ساختاری که در درس «پوشههای خصوصی و ساختاردهی فایلها» ساختیم
نقشهی مسیرهای DevNotes
اسکلت اولیهی پروژه این مسیرها را دارد:
| آدرس | گروه | محتوا |
|---|---|---|
/ |
(public) |
صفحهی اصلی |
/about |
(public) |
دربارهی پروژه |
/posts |
(public) |
فهرست نوشتهها (فعلاً موقت) |
/login |
(auth) |
صفحهی ورود (فعلاً بدون فرم) |
/register |
(auth) |
صفحهی ثبتنام (فعلاً بدون فرم) |
/dashboard |
(dashboard) |
صفحهی اصلی داشبورد |
/dashboard/posts |
(dashboard) |
فهرست نوشتههای مدیریت (فعلاً موقت) |
/dashboard/posts/new |
(dashboard) |
صفحهی ایجاد نوشته (فعلاً بدون فرم) |
صفحهی جزئیات هر نوشته (/posts/[slug]) را در درس «ساخت فهرست و صفحهی جزئیات نوشتهها» از فصل «Routing و Navigation» اضافه میکنیم، چون به مسیرهای پویا نیاز دارد.
تغییراتی نسبت به ساختار قبلی
در درس «Route Groups و سازماندهی مسیرها» گفتیم محل نهایی صفحهی ایجاد نوشته را اینجا مشخص میکنیم. ایجاد نوشته کار مدیریت است و باید پشت ورود کاربر قرار بگیرد، پس آن را از بخش عمومی به داشبورد منتقل میکنیم:
- حذف: پوشهی
app/(public)/posts/new(آدرس/posts/newدیگر وجود ندارد). - جدید:
app/(dashboard)/dashboard/posts/new/page.tsxبا آدرس/dashboard/posts/new. - فایلهای دیگر تغییرکرده:
components/SiteHeader.tsx،app/(public)/page.tsx،app/(dashboard)/layout.tsxوapp/(dashboard)/dashboard/page.tsx. - فایلهای جدید: گروه
(auth)و صفحهیdashboard/posts.
یادآوری: قرارگرفتن در گروه (dashboard) هنوز هیچ محافظتی ایجاد نمیکند. کنترل دسترسی واقعی را در فصل «Authentication و Security» و «Proxy و کنترل دسترسی» میسازیم.
یک ابزار جدید: کامپوننت Link
برای اسکلت به لینک بین صفحات نیاز داریم. در Next.js برای لینک داخلی بهجای تگ <a> از کامپوننت Link استفاده میکنیم:
import Link from "next/link";
<Link href="/posts">نوشتهها</Link>
Link ناوبری سریع بدون بارگذاری کامل صفحه انجام میدهد. فعلاً همین مقدار کافی است؛ کارکرد کامل آن، از جمله Prefetch، را در درس «لینکسازی با Link» میبینیم.
گام ۱: هدر با ناوبری
فایل components/SiteHeader.tsx را اینطور بازنویسی کنید:
// components/SiteHeader.tsx
import Link from "next/link";
export default function SiteHeader() {
return (
<header>
<strong>DevNotes</strong>
<nav aria-label="ناوبری اصلی">
<ul>
<li><Link href="/">خانه</Link></li>
<li><Link href="/posts">نوشتهها</Link></li>
<li><Link href="/about">درباره</Link></li>
<li><Link href="/login">ورود</Link></li>
</ul>
</nav>
</header>
);
}گام ۲: صفحهی اصلی
// app/(public)/page.tsx
import Link from "next/link";
export default function HomePage() {
return (
<main>
<h1>DevNotes</h1>
<p>
وبسایتی برای انتشار و مدیریت نوشتههای فنی. در طول دورهی
Next.js 16 این پروژه را قدمبهقدم کامل میکنیم.
</p>
<Link href="/posts">مشاهدهی نوشتهها</Link>
</main>
);
}
صفحههای about و posts را بدون تغییر نگه دارید.
گام ۳: گروه (auth) برای ورود و ثبتنام
صفحات ورود و ثبتنام نه به هدر و فوتر عمومی و نه به منوی کناری داشبورد نیاز دارند، پس گروه جداگانهای برایشان میسازیم. پوشهی app/(auth) را بسازید:
// app/(auth)/layout.tsx
import type { ReactNode } from "react";
import Link from "next/link";
export default function AuthLayout({
children,
}: Readonly<{ children: ReactNode }>) {
return (
<div>
<p>
<Link href="/">بازگشت به DevNotes</Link>
</p>
{children}
</div>
);
}// app/(auth)/login/page.tsx
import Link from "next/link";
export default function LoginPage() {
return (
<main>
<h1>ورود</h1>
<p>فرم ورود در فصلهای بعد ساخته میشود.</p>
<p>
حساب کاربری ندارید؟ <Link href="/register">ثبتنام</Link>
</p>
</main>
);
}// app/(auth)/register/page.tsx
import Link from "next/link";
export default function RegisterPage() {
return (
<main>
<h1>ثبتنام</h1>
<p>فرم ثبتنام در فصلهای بعد ساخته میشود.</p>
<p>
قبلاً ثبتنام کردهاید؟ <Link href="/login">ورود</Link>
</p>
</main>
);
}گام ۴: داشبورد
ابتدا Layout داشبورد را با لینکهای ناوبری بهروزرسانی کنید:
// app/(dashboard)/layout.tsx
import type { ReactNode } from "react";
import Link from "next/link";
export default function DashboardLayout({
children,
}: Readonly<{ children: ReactNode }>) {
return (
<div>
<aside>
<strong>داشبورد DevNotes</strong>
<nav aria-label="ناوبری داشبورد">
<ul>
<li><Link href="/dashboard">نمای کلی</Link></li>
<li><Link href="/dashboard/posts">نوشتههای من</Link></li>
<li><Link href="/dashboard/posts/new">نوشتهی جدید</Link></li>
<li><Link href="/">بازگشت به سایت</Link></li>
</ul>
</nav>
</aside>
{children}
</div>
);
}// app/(dashboard)/dashboard/page.tsx
export default function DashboardPage() {
return (
<main>
<h1>داشبورد مدیریت</h1>
<p>در این بخش نوشتههای خودتان را مدیریت خواهید کرد.</p>
</main>
);
}// app/(dashboard)/dashboard/posts/page.tsx
export default function DashboardPostsPage() {
return (
<main>
<h1>نوشتههای من</h1>
<p>فهرست نوشتههای شما بعد از اتصال به داده در اینجا نمایش داده میشود.</p>
</main>
);
}// app/(dashboard)/dashboard/posts/new/page.tsx
export default function NewPostPage() {
return (
<main>
<h1>نوشتهی جدید</h1>
<p>فرم ایجاد نوشته در فصل «فرمها و Server Actions» ساخته میشود.</p>
</main>
);
}
حالا پوشهی قدیمی app/(public)/posts/new را حذف کنید.
ساختار نهایی پروژه در پایان فصل دوم
devnotes/
├── app/
│ ├── layout.tsx ← Root Layout
│ ├── not-found.tsx ← ۴۰۴ فارسی
│ ├── globals.css
│ ├── favicon.ico
│ ├── (public)/
│ │ ├── layout.tsx ← هدر و فوتر
│ │ ├── page.tsx ← /
│ │ ├── about/
│ │ │ └── page.tsx ← /about
│ │ └── posts/
│ │ ├── layout.tsx
│ │ ├── page.tsx ← /posts
│ │ └── _components/
│ │ └── PostsIntro.tsx
│ ├── (auth)/
│ │ ├── layout.tsx
│ │ ├── login/
│ │ │ └── page.tsx ← /login
│ │ └── register/
│ │ └── page.tsx ← /register
│ └── (dashboard)/
│ ├── layout.tsx ← منوی کناری
│ └── dashboard/
│ ├── page.tsx ← /dashboard
│ └── posts/
│ ├── page.tsx ← /dashboard/posts
│ └── new/
│ └── page.tsx ← /dashboard/posts/new
└── components/
├── SiteHeader.tsx
└── SiteFooter.tsxتوضیح طراحی
- سه گروه، سه Layout: بخش عمومی هدر و فوتر دارد، بخش ورود فقط یک لینک بازگشت، و داشبورد منوی کناری. Root Layout فقط ساختار HTML و تنظیمات سراسری را نگه میدارد.
-
همه Server Component هستند: هیچ فایلی
"use client"ندارد و هنوز هم نیازی نیست. نیاز به Client Component را در فصل «Server Components و Client Components» میبینیم. -
HTML معنایی: از
navبا ویژگیaria-label،ul،main،headerوasideاستفاده کردهایم تا ساختار برای صفحهخوانها و موتورهای جستوجو معنادار باشد. - بدون استایل: چیدمان فعلاً ساده و عمودی است. استایلدهی راستچین را در درس «طراحی رابط کاربری فارسی و راستچین DevNotes» انجام میدهیم.
خروجی و رفتار مورد انتظار
- سرور را با
npm run devاجرا کنید (اگر قبلاً اجرا بود و صفحهای ۴۰۴ داد، یکبار آن را متوقف و دوباره اجرا کنید). - در
/هدر با چهار لینک و دکمهی «مشاهدهی نوشتهها» را ببینید. - از هدر به
/postsو/aboutبروید؛ هدر و فوتر ثابت میمانند. - از هدر روی «ورود» بزنید؛ در
/loginهدر و فوتر عمومی نیستند و فقط لینک بازگشت دیده میشود. - آدرس
/dashboardرا باز کنید؛ منوی کناری داشبورد را میبینید و با لینکهای آن به/dashboard/postsو/dashboard/posts/newمیروید. - آدرس
/posts/newرا باز کنید؛ حالا صفحهی ۴۰۴ فارسی نمایش داده میشود.
نکتههای مهم
-
لینک داخلی را با
Linkبنویسید. اگر برای لینک داخلی از تگ<a>استفاده کنید، ESLint در Next.js هشدار میدهد و ناوبری سریع هم از بین میرود. -
برای داشبورد هنوز محافظتی نداریم. هر کسی میتواند
/dashboardرا باز کند. این محدودیت آموزشی و موقتی است و در فصلهای امنیتی برطرف میشود. -
مسیرهای پویا را هنوز نساختهایم. صفحهی جزئیات نوشته به
paramsنیاز دارد و موضوع فصل بعد است. - در تغییرات بعدی ساختار، دلیل و فایلهای متأثر را بررسی کنید. مثل همین درس که بهخاطر انتقال صفحهی ایجاد نوشته، چند فایل را همزمان تغییر دادیم.
جمعبندی
- اسکلت DevNotes از سه Route Group ساخته شده:
(public)برای سایت،(auth)برای ورود و ثبتنام و(dashboard)برای مدیریت. - هر گروه Layout مخصوص خودش را دارد و Root Layout فقط ساختار HTML را نگه میدارد.
- صفحهی ایجاد نوشته به
/dashboard/posts/newمنتقل شد، چون به کاربر واردشده تعلق دارد. - برای لینک داخلی از
Linkاستفاده کردیم و جزئیات آن در درس بعدی فصل «Routing و Navigation» میآید. - هنوز داده، فرم، استایل، مسیر پویا و کنترل دسترسی نداریم؛ اینها در فصلهای بعد به همین ساختار اضافه میشوند.
- در درس بعد، «آزمون فصل دوم»، دانستههای این فصل را میسنجیم.
