فارسی

سیستم مسیریابی متحول‌کننده مبتنی بر فایل در پوشه App در Next.js را کاوش کنید که سازماندهی، عملکرد و تجربه توسعه‌دهنده بهتری را برای برنامه‌های وب مدرن ارائه می‌دهد.

پوشه App در Next.js: انقلابی در مسیریابی مبتنی بر فایل

Next.js همواره مرزهای توسعه وب را جابجا کرده و ابزارها و ویژگی‌های قدرتمندی را برای ساخت برنامه‌های کاربردی با عملکرد بالا، مقیاس‌پذیر و کاربرپسند به توسعه‌دهندگان ارائه داده است. معرفی پوشه App یک جهش بزرگ به جلو محسوب می‌شود، به‌ویژه در رویکرد نوآورانه‌اش به مسیریابی مبتنی بر فایل. این مقاله به طور عمیق به مکانیزم مسیریابی پوشه App می‌پردازد و مزایا، مفاهیم کلیدی و پیامدهای عملی آن را برای ساخت برنامه‌های وب مدرن با Next.js بررسی می‌کند.

درک تکامل مسیریابی در Next.js

قبل از پوشه App، Next.js برای مسیریابی به پوشه Pages متکی بود. این رویکرد با اینکه مؤثر بود، محدودیت‌های خاصی داشت. پوشه Pages از یک سیستم مسیریابی ساده مبتنی بر فایل استفاده می‌کرد که در آن هر فایل در دایرکتوری `pages` به یک مسیر متناظر بود. برای مثال، `pages/about.js` به مسیر `/about` نگاشت می‌شد.

با اینکه این روش سرراست بود، پوشه Pages فاقد پشتیبانی داخلی برای لایوت‌های پیچیده، استراتژی‌های واکشی داده و الگوهای رندر سمت سرور بود و اغلب توسعه‌دهندگان را مجبور می‌کرد این ویژگی‌ها را به صورت دستی پیاده‌سازی کنند. علاوه بر این، ارتباط تنگاتنگ بین واکشی داده و رندر کامپوننت گاهی اوقات می‌توانست منجر به گلوگاه‌های عملکردی شود.

پوشه App با معرفی یک سیستم مسیریابی انعطاف‌پذیرتر و قدرتمندتر که بر پایه کامپوننت‌های سرور ری‌اکت (React Server Components)، لایوت‌ها و سایر ویژگی‌های پیشرفته ساخته شده است، این محدودیت‌ها را برطرف می‌کند. این سیستم فراتر از یک نگاشت ساده فایل به مسیر می‌رود و رویکردی اعلانی‌تر (declarative) و ترکیبی‌تر (composable) برای تعریف مسیرها و لایوت‌های برنامه ارائه می‌دهد.

معرفی پوشه App: پارادایمی جدید برای مسیریابی

پوشه App که در ریشه پروژه Next.js شما و در داخل پوشه `app` قرار دارد، رویکردی اساساً متفاوت به مسیریابی را معرفی می‌کند. به جای نگاشت مستقیم فایل‌ها به مسیرها، پوشه App از یک سیستم مبتنی بر قرارداد استفاده می‌کند که در آن ساختار دایرکتوری‌ها و فایل‌های ویژه، مسیرهای برنامه را تعیین می‌کنند.

این رویکرد چندین مزیت کلیدی ارائه می‌دهد:

مفاهیم کلیدی در سیستم مسیریابی پوشه App

برای استفاده مؤثر از سیستم مسیریابی پوشه App، درک مفاهیم کلیدی که زیربنای عملکرد آن را تشکیل می‌دهند، ضروری است:

۱. سگمنت‌های مسیر و پوشه‌ها

هر پوشه در داخل دایرکتوری `app` یک سگمنت مسیر را نشان می‌دهد. نام پوشه با بخش مسیر در URL مطابقت دارد. برای مثال، یک ساختار پوشه `app/blog/posts` به مسیر `/blog/posts` نگاشت می‌شود.

این ساختار را در نظر بگیرید:

app/
  blog/
    posts/
      page.js

این ساختار یک مسیر در `/blog/posts` را تعریف می‌کند. فایل `page.js` در داخل پوشه `posts` کامپوننت سگمنت مسیر است که محتوای آن مسیر را رندر می‌کند.

۲. فایل `page.js`: رندر کردن محتوای مسیر

فایل page.js (یا page.tsx برای تایپ‌اسکریپت) یک فایل ویژه است که محتوای قابل رندر برای یک سگمنت مسیر خاص را تعریف می‌کند. این فایل نقطه ورود آن مسیر است و باید یک کامپوننت ری‌اکت را به عنوان خروجی پیش‌فرض خود (default export) صادر کند.

مثال:

// app/blog/posts/page.js

export default function PostsPage() {
  return (
    <div>
      <h1>پست‌های وبلاگ</h1>
      <p>لیست پست‌های وبلاگ در اینجا نمایش داده خواهد شد.</p>
    </div>
  );
}

۳. لایوت‌ها: تعریف رابط کاربری مشترک

لایوت‌ها به شما امکان می‌دهند رابط کاربری‌ای را تعریف کنید که در چندین صفحه یا سگمنت مسیر به اشتراک گذاشته می‌شود. یک لایوت می‌تواند شامل عناصری مانند هدر، فوتر، سایدبار یا هر کامپوننت دیگری باشد که باید در یک بخش از برنامه شما یکسان باشد. لایوت‌ها با استفاده از فایل `layout.js` (یا `layout.tsx`) تعریف می‌شوند.

لایوت‌ها تو در تو هستند. این بدان معناست که لایوت ریشه (`app/layout.js`) کل برنامه را در بر می‌گیرد و لایوت‌های تو در توی دیگر، سگمنت‌های مسیر خاصی را پوشش می‌دهند. هنگام جابجایی بین مسیرهایی که یک لایوت مشترک دارند، Next.js حالت لایوت را حفظ کرده و از رندر مجدد آن جلوگیری می‌کند، که منجر به بهبود عملکرد و تجربه کاربری روان‌تر می‌شود.

مثال:

// app/layout.js

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <header>
          <nav>
            <a href="/">خانه</a> |
            <a href="/blog">وبلاگ</a>
          </nav>
        </header>
        <main>{children}</main>
        <footer>
          <p>حق کپی رایت 2023</p>
        </footer>
      </body>
    </html>
  );
}

در این مثال، `RootLayout` ساختار پایه HTML، هدر، فوتر و ناوبری را برای کل برنامه تعریف می‌کند. هر صفحه‌ای که در دایرکتوری `app` رندر شود، توسط این لایوت پوشش داده خواهد شد.

۴. تمپلیت‌ها: حفظ حالت بین مسیرها

مشابه لایوت‌ها، تمپلیت‌ها نیز مسیرهای فرزند را در بر می‌گیرند. با این حال، برخلاف لایوت‌ها، تمپلیت‌ها برای هر مسیر فرزند یک نمونه کامپوننت جدید ایجاد می‌کنند. این بدان معناست که حالت تمپلیت هنگام جابجایی بین مسیرهای داخل آن حفظ نمی‌شود. تمپلیت‌ها برای سناریوهایی مفید هستند که نیاز به بازنشانی یا مقداردهی اولیه مجدد حالت در هنگام انتقال بین مسیرها دارید. برای ایجاد تمپلیت‌ها از template.js (یا template.tsx) استفاده کنید.

۵. گروه‌های مسیر: سازماندهی مسیرها بدون سگمنت‌های URL

گروه‌های مسیر به شما این امکان را می‌دهند که مسیرهای خود را در داخل پوشه App سازماندهی کنید بدون اینکه ساختار URL تحت تأثیر قرار گیرد. گروه‌های مسیر با قرار دادن نام پوشه در داخل پرانتز تعریف می‌شوند، به عنوان مثال، `(group-name)`. این پرانتزها به Next.js می‌گویند که این پوشه را به عنوان یک مکانیزم گروه‌بندی منطقی در نظر بگیرد و نه یک سگمنت مسیر.

این ویژگی به ویژه برای سازماندهی برنامه‌های بزرگ با مسیرهای زیاد مفید است. به عنوان مثال، ممکن است از گروه‌های مسیر برای جدا کردن بخش‌های مختلف برنامه خود استفاده کنید، مانند `(marketing)` و `(app)`. این گروه‌ها فقط بر ساختار فایل تأثیر می‌گذارند، نه بر مسیرهای URL.

مثال:

app/
  (marketing)/
    home/
      page.js  // قابل دسترس در /home
    about/
      page.js  // قابل دسترس در /about
  (app)/
    dashboard/
      page.js  // قابل دسترس در /dashboard

۶. مسیرهای پویا: مدیریت سگمنت‌های متغیر

مسیرهای پویا به شما امکان می‌دهند مسیرهایی با سگمنت‌های متغیر ایجاد کنید. این برای سناریوهایی مفید است که نیاز به تولید مسیرها بر اساس داده‌ها دارید، مانند پست‌های وبلاگ، صفحات محصول یا پروفایل‌های کاربری. سگمنت‌های مسیر پویا با قرار دادن نام سگمنت در داخل براکت تعریف می‌شوند، به عنوان مثال، `[id]`. `id` یک پارامتر را نشان می‌دهد که می‌توان در کامپوننت `page.js` به آن دسترسی داشت.

مثال:

app/
  blog/
    [slug]/
      page.js

در این مثال، `[slug]` یک سگمنت مسیر پویا است. یک URL مانند `/blog/my-first-post` با این مسیر مطابقت خواهد داشت و پارامتر `slug` به `my-first-post` تنظیم می‌شود. شما می‌توانید با استفاده از پراپ `params` به پارامتر `slug` در کامپوننت `page.js` دسترسی پیدا کنید.

// app/blog/[slug]/page.js

export default function BlogPost({ params }) {
  const { slug } = params;
  return (
    <div>
      <h1>پست وبلاگ: {slug}</h1>
      <p>محتوای پست وبلاگ با اسلاگ: {slug}</p>
    </div>
  );
}

شما باید مقادیر ممکن برای این مسیرهای پویا را تولید کنید. Next.js تابع `generateStaticParams` را برای تولید سایت استاتیک (SSG) و رندر سمت سرور (SSR) فراهم می‌کند. این تابع به شما اجازه می‌دهد مشخص کنید کدام مسیرهای پویا باید در زمان ساخت (build time) از پیش رندر شوند.

// app/blog/[slug]/page.js

export async function generateStaticParams() {
  const posts = [
    { slug: 'my-first-post' },
    { slug: 'my-second-post' },
  ];

  return posts.map((post) => ({ slug: post.slug }));
}

export default function BlogPost({ params }) {
  const { slug } = params;
  return (
    <div>
      <h1>پست وبلاگ: {slug}</h1>
      <p>محتوای پست وبلاگ با اسلاگ: {slug}</p>
    </div>
  );
}

۷. سگمنت‌های Catch-All: مدیریت مسیرهای ناشناخته

سگمنت‌های Catch-all نوعی از مسیرهای پویا هستند که به شما امکان می‌دهند هر تعداد سگمنت را در یک URL مطابقت دهید. آنها با افزودن سه نقطه به ابتدای نام سگمنت تعریف می‌شوند، به عنوان مثال، `[...path]`. سگمنت‌های Catch-all برای ایجاد مسیرهای انعطاف‌پذیر که می‌توانند انواع ساختارهای URL را مدیریت کنند، مفید هستند.

مثال:

app/
  docs/
    [...path]/
      page.js

در این مثال، `[...path]` یک سگمنت catch-all است. URLهایی مانند `/docs/introduction`، `/docs/api/reference` و `/docs/examples/basic` همگی با این مسیر مطابقت خواهند داشت. پارامتر `path` یک آرایه حاوی سگمنت‌های مطابقت داده شده خواهد بود.

// app/docs/[...path]/page.js

export default function DocsPage({ params }) {
  const { path } = params;
  return (
    <div>
      <h1>مستندات</h1>
      <p>مسیر: {path.join('/')}</p>
    </div>
  );
}

۸. مسیرهای موازی: رندر همزمان چندین صفحه

مسیرهای موازی شما را قادر می‌سازند تا چندین صفحه را به طور همزمان در یک لایوت رندر کنید. این ویژگی به ویژه برای ایجاد الگوهای رابط کاربری پیچیده، مانند داشبوردها با چندین پنل یا دیالوگ‌های مودال که بالای صفحه فعلی ظاهر می‌شوند، مفید است. مسیرهای موازی با استفاده از نماد @ تعریف می‌شوند، به عنوان مثال، `@children`، `@modal`. آنها می‌توانند مستقیماً در URL مشخص شوند یا با استفاده از هوک `useRouter` به آنها ناوبری کرد.

مثال:

app/
  @children/
    page.js // محتوای اصلی را رندر می‌کند
  @modal/
    login/
      page.js // مودال لاگین را رندر می‌کند

برای نمایش مسیرهای موازی، از کامپوننت `` استفاده کنید.

۹. رهگیری مسیرها: ایجاد انتقال‌های رابط کاربری پیشرفته

رهگیری مسیرها به شما این امکان را می‌دهد که یک مسیر را از بخش دیگری از برنامه خود در چارچوب مسیر فعلی بارگذاری کنید. این می‌تواند برای ایجاد انتقال‌های رابط کاربری پیشرفته، مانند نمایش یک دیالوگ مودال هنگام کلیک روی یک لینک بدون ترک صفحه فعلی، استفاده شود. آنها با استفاده از سینتکس (...) تعریف می‌شوند.

واکشی داده در پوشه App

پوشه App روش‌های جدید و بهبود یافته‌ای برای واکشی داده معرفی می‌کند، با بهره‌گیری از کامپوننت‌های سرور ری‌اکت و API `fetch` با قابلیت‌های داخلی کشینگ و اعتبارسنجی مجدد. این منجر به عملکرد بهتر و تجربه توسعه روان‌تر می‌شود. هم کامپوننت‌های سرور و هم کلاینت می‌توانند داده‌ها را واکشی کنند، اما استراتژی متفاوت است.

۱. واکشی داده در کامپوننت‌های سرور

کامپوننت‌های سرور، که پیش‌فرض در پوشه App هستند، می‌توانند مستقیماً داده‌ها را از پایگاه‌های داده یا APIها واکشی کنند. این کار در داخل تابع کامپوننت قبل از رندر شدن انجام می‌شود. از آنجا که کامپوننت‌های سرور در سرور اجرا می‌شوند، می‌توانید با خیال راحت کلیدهای مخفی و اعتبارسنجی‌ها را بدون افشای آنها به کلاینت در کد قرار دهید. API `fetch` به طور خودکار memoize می‌شود، به این معنی که درخواست‌های داده یکسان، تکراری‌زدایی می‌شوند که عملکرد را بیشتر بهبود می‌بخشد.

// app/page.js

async function getData() {
  const res = await fetch('https://jsonplaceholder.typicode.com/todos/1');
  // مقدار بازگشتی سریالایز *نمی‌شود*
  // شما می‌توانید Date, Map, Set و غیره را بازگردانید.

  if (!res.ok) {
    // این نزدیکترین Error Boundary `error.js` را فعال می‌کند
    throw new Error('واکشی داده ناموفق بود');
  }

  return res.json();
}

export default async function Page() {
  const data = await getData();

  return <div>{data.title}</div>;
}

۲. واکشی داده در کامپوننت‌های کلاینت

کامپوننت‌های کلاینت، که با دستور 'use client' در بالای فایل مشخص می‌شوند، در مرورگر کاربر اجرا می‌شوند. واکشی داده در کامپوننت‌های کلاینت معمولاً شامل استفاده از هوک `useEffect` و کتابخانه‌ای مانند `axios` یا API `fetch` است. Server Actions یک راه امن برای تغییر داده‌های سرور از کامپوننت‌های کلاینت فراهم می‌کنند. این روش راهی امن برای تعامل کامپوننت‌های کلاینت با داده‌های سرور بدون افشای مستقیم نقاط پایانی API ارائه می‌دهد.

// app/components/ClientComponent.js
'use client';

import { useState, useEffect } from 'react';

export default function ClientComponent() {
  const [data, setData] = useState(null);

  useEffect(() => {
    async function fetchData() {
      const res = await fetch('https://jsonplaceholder.typicode.com/todos/1');
      const data = await res.json();
      setData(data);
    }

    fetchData();
  }, []);

  if (!data) {
    return <div>در حال بارگذاری...</div>;
  }

  return <div>{data.title}</div>;
}

ملاحظات سئو با پوشه App

رویکرد "سرور-اول" در پوشه App مزایای قابل توجهی برای سئو دارد. از آنجا که محتوا در سرور رندر می‌شود، خزنده‌های موتورهای جستجو می‌توانند به راحتی به محتوای صفحه دسترسی پیدا کرده و آن را ایندکس کنند. در اینجا برخی از ملاحظات کلیدی سئو آورده شده است:

مزایای استفاده از سیستم مسیریابی پوشه App

سیستم مسیریابی پوشه App مزایای بی‌شماری را ارائه می‌دهد که فرآیند توسعه را بهبود بخشیده، عملکرد برنامه را افزایش داده و به تجربه کاربری بهتر کمک می‌کند. بیایید این مزایا را با جزئیات بیشتری بررسی کنیم:

مثال‌های عملی از مسیریابی پوشه App در عمل

برای نشان دادن قدرت و انعطاف‌پذیری سیستم مسیریابی پوشه App، بیایید چند مثال عملی را در نظر بگیریم:

۱. ساخت یک وبلاگ ساده با مسیرهای پویا

یک برنامه وبلاگ را در نظر بگیرید که هر پست آن URL منحصر به فرد خود را بر اساس اسلاگ خود دارد. با پوشه App، این کار به راحتی با استفاده از مسیرهای پویا قابل پیاده‌سازی است:

app/
  blog/
    [slug]/
      page.js

دایرکتوری `[slug]` یک سگمنت مسیر پویا را نشان می‌دهد که با هر URL تحت مسیر `/blog/` مطابقت خواهد داشت. فایل `page.js` در داخل دایرکتوری `[slug]` محتوای پست وبلاگ مربوطه را رندر خواهد کرد.

// app/blog/[slug]/page.js

export async function generateStaticParams() {
  // تمام پست‌های وبلاگ را از پایگاه داده یا API واکشی کنید
  const posts = await fetchPosts();

  // پست‌ها را به آرایه‌ای از پارامترهای اسلاگ نگاشت کنید
  return posts.map((post) => ({ slug: post.slug }));
}

export default async function BlogPost({ params }) {
  const { slug } = params;

  // پست وبلاگ با اسلاگ منطبق را واکشی کنید
  const post = await fetchPost(slug);

  if (!post) {
    return <div>پست یافت نشد</div>;
  }

  return (
    <article>
      <h1>{post.title}</h1>
      <p>{post.content}</p>
    </article>
  );
}

این مثال نشان می‌دهد که چگونه می‌توان از مسیرهای پویا برای ایجاد صفحات جداگانه برای هر پست وبلاگ به روشی ساده و کارآمد استفاده کرد.

۲. پیاده‌سازی یک دیالوگ مودال با مسیرهای رهگیری

فرض کنید می‌خواهید یک دیالوگ مودال پیاده‌سازی کنید که وقتی کاربر روی یک لینک کلیک می‌کند، بدون ترک صفحه فعلی ظاهر شود. این کار را می‌توان با استفاده از مسیرهای رهگیری انجام داد:

app/
  (.)photos/
    [id]/
      @modal/
        page.js
  page.js

در اینجا، `(.)photos/[id]/@modal/page.js` درخواست‌هایی که از صفحه فعلی به `photos/[id]` می‌روند را رهگیری می‌کند. وقتی کاربر روی لینکی به یک عکس خاص کلیک می‌کند، دیالوگ مودال به جای انتقال به یک صفحه جدید، بالای صفحه فعلی ظاهر می‌شود.

۳. ایجاد یک لایوت داشبورد با مسیرهای موازی

تصور کنید در حال ساخت یک برنامه داشبورد با چندین پنل هستید که باید به طور همزمان رندر شوند. می‌توان از مسیرهای موازی برای دستیابی به این لایوت استفاده کرد:

app/
  @analytics/
    page.js       // داشبورد تحلیل
  @settings/
    page.js       // پنل تنظیمات
  page.js           // لایوت اصلی داشبورد

در این ساختار، `@analytics` و `@settings` مسیرهای موازی را نشان می‌دهند که در لایوت اصلی داشبورد رندر خواهند شد. هر مسیر موازی فایل `page.js` خود را دارد که محتوای آن پنل را تعریف می‌کند. لایوت می‌تواند با استفاده از کامپوننت <Slot> تصمیم بگیرد که آنها را در کجا قرار دهد.

مهاجرت از پوشه Pages به پوشه App

مهاجرت یک برنامه Next.js موجود از پوشه Pages به پوشه App نیازمند برنامه‌ریزی و اجرای دقیق است. در حالی که پوشه App مزایای قابل توجهی را ارائه می‌دهد، مفاهیم و الگوهای جدیدی را نیز معرفی می‌کند که توسعه‌دهندگان باید آنها را درک کنند. در اینجا یک راهنمای گام به گام برای کمک به شما در فرآیند مهاجرت آورده شده است:

  1. تفاوت‌های کلیدی را درک کنید: قبل از شروع مهاجرت، اطمینان حاصل کنید که تفاوت‌های کلیدی بین پوشه Pages و پوشه App را به طور کامل درک کرده‌اید، از جمله سیستم مسیریابی، واکشی داده و معماری کامپوننت‌ها.
  2. یک دایرکتوری `app` ایجاد کنید: یک دایرکتوری جدید به نام `app` در ریشه پروژه Next.js خود ایجاد کنید. این دایرکتوری تمام کامپوننت‌ها و مسیرهایی را که بخشی از پوشه App هستند، در خود جای خواهد داد.
  3. مسیرها را به تدریج مهاجرت دهید: با مهاجرت تدریجی مسیرها، یکی پس از دیگری، شروع کنید. این به شما امکان می‌دهد هر مسیر را به صورت جداگانه آزمایش و اشکال‌زدایی کنید و خطر بروز خطا را به حداقل برسانید.
  4. کامپوننت‌ها را به کامپوننت‌های سرور تبدیل کنید: در صورت امکان، کامپوننت‌های ری‌اکت موجود خود را به کامپوننت‌های سرور تبدیل کنید. این کار عملکرد را بهبود بخشیده و میزان جاوااسکریپتی را که باید در مرورگر دانلود و اجرا شود، کاهش می‌دهد.
  5. منطق واکشی داده را به‌روز کنید: منطق واکشی داده خود را برای بهره‌مندی از قابلیت‌های داخلی واکشی داده در پوشه App به‌روز کنید. این ممکن است شامل انتقال کد واکشی داده از کامپوننت‌های کلاینت به کامپوننت‌های سرور باشد.
  6. لایوت‌ها و تمپلیت‌ها را پیاده‌سازی کنید: لایوت‌ها و تمپلیت‌ها را برای تعریف عناصر رابط کاربری مشترک که در چندین صفحه یکسان هستند، پیاده‌سازی کنید.
  7. به طور کامل تست کنید: هر مسیر مهاجرت داده شده را به طور کامل تست کنید تا اطمینان حاصل شود که به درستی کار می‌کند و هیچ پسرفتی وجود ندارد.
  8. دایرکتوری `pages` را حذف کنید: پس از مهاجرت تمام مسیرها، می‌توانید دایرکتوری `/pages` را حذف کنید.

نتیجه‌گیری

پوشه App در Next.js نمایانگر یک تکامل قابل توجه در مسیریابی مبتنی بر فایل است و به توسعه‌دهندگان راهی سازمان‌یافته‌تر، با عملکرد بهتر و انعطاف‌پذیرتر برای ساخت برنامه‌های وب مدرن ارائه می‌دهد. با درک مفاهیم کلیدی و پذیرش ویژگی‌های جدید، توسعه‌دهندگان می‌توانند از پوشه App برای ایجاد تجربیات کاربری استثنایی و دستیابی به بهره‌وری بیشتر استفاده کنند. آینده توسعه Next.js در پوشه App نهفته است و پذیرش آن یک حرکت استراتژیک برای ساخت برنامه‌های وب پیشرفته است. این ابزاری قدرتمند برای توسعه‌دهندگان در سراسر جهان است.

همانطور که اکوسیستم Next.js به تکامل خود ادامه می‌دهد، پوشه App آماده است تا به استاندارد ساخت برنامه‌های وب قوی، مقیاس‌پذیر و با عملکرد بالا تبدیل شود. این تغییر را بپذیرید، امکانات را کاوش کنید و پتانسیل کامل Next.js را آزاد کنید!