فرانت اند

ویژگی‌های Next.js 13 و معماری جدید App Router

نسخه‌ی ۱۳ نکست فقط چند API تازه اضافه نکرد؛ مدل ساخت صفحه را عوض کرد. اگر پروژه‌تان هنوز روی <code>pages/</code> است، این مرور نشان می‌دهد ویژگی‌های Next.js 13 کجا واقعاً به درد می‌خورند و کجا باید احتیاط کنید.

  • نویسنده تیم تتا
  • ۸ دقیقه مطالعه
  • به‌روزرسانی:
ویژگی‌های Next.js 13 و معماری جدید App Router

Next.js 13 چه چیزی را عوض کرد؟

تا نسخه‌ی ۱۲، مسیریابی بر پایه‌ی pages/ بود و هر صفحه یک کامپوننت ریکت کامل بود که در مرورگر هم hydrate می‌شد. نسخه‌ی ۱۳ پوشه‌ی app/ را معرفی کرد که روی قابلیت‌های جدید ریکت (Server Components و Suspense) ساخته شده است؛ جاوااسکریپت فقط برای بخش‌هایی ارسال می‌شود که واقعاً تعامل دارند.

کنار این مدل تازه، چند بهبود مستقل هم آمد: next/font برای بارگذاری فونت بدون پرش چیدمان، کامپوننت Image سبک‌تر، و کامپوننت Link که دیگر به تگ <a> داخلی نیاز ندارد. Turbopack هم به‌عنوان جایگزین آینده‌ی webpack معرفی شد. جزئیات هر نسخه را همیشه در مستندات رسمی Next.js با نسخه‌ی خودتان تطبیق دهید، چون پیش‌فرض‌ها در نسخه‌های بعدی تغییر کرده‌اند.

App Router و قرارداد فایل‌ها

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

فایل‌های خاص در پوشه‌ی app
فایلنقش
page.tsxرابط یکتای آن مسیر؛ بدون آن مسیر عمومی نیست.
layout.tsxپوسته‌ی مشترک برای همه‌ی زیرمسیرها؛ هنگام ناوبری دوباره mount نمی‌شود.
loading.tsxرابط موقت تا آماده‌شدن داده (روی Suspense ساخته شده).
error.tsxمرز خطا برای همان بخش، بدون خراب‌شدن کل صفحه.
route.tsRoute Handler برای ساخت API با متدهای GET، POST و مانند آن.

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

ts
// app/layout.tsx
export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="fa" dir="rtl">
      <body>
        <header>TETA</header>
        {children}
      </body>
    </html>
  );
}

Server Components در برابر Client Components

کامپوننت سروری فقط روی سرور اجرا می‌شود؛ می‌تواند مستقیم از پایگاه‌داده بخواند، کلید محرمانه استفاده کند و هیچ بایتی از کدش به مرورگر نمی‌رود. در عوض نمی‌تواند useState، useEffect یا رویدادهایی مثل onClick داشته باشد. هر جا به این‌ها نیاز دارید، بالای فایل "use client" بنویسید.

ts
// app/products/page.tsx  (Server Component)
import AddToCart from './AddToCart';

export default async function Products() {
  const res = await fetch('https://api.example.com/products');
  const products: { id: number; name: string }[] = await res.json();
  return (
    <ul>
      {products.map(p => (
        <li key={p.id}>{p.name} <AddToCart id={p.id} /></li>
      ))}
    </ul>
  );
}

// app/products/AddToCart.tsx
'use client';
import { useState } from 'react';
export default function AddToCart({ id }: { id: number }) {
  const [added, setAdded] = useState(false);
  return <button onClick={() => setAdded(true)}>{added ? 'Added' : 'Add'}</button>;
}
تصویر انتزاعی از دو لایه‌ی رنگی، سرور و مرورگر، که قطعه‌های کوچک کد بین آن‌ها جریان دارند
هر چه منطق بیشتری در سرور بماند، بسته‌ی جاوااسکریپتِ ارسالی به کاربر سبک‌تر می‌شود.

Streaming و حالت‌های بارگذاری

در مدل قدیمی، یک پرس‌وجوی کند کل صفحه را نگه می‌داشت. حالا با افزودن یک فایل loading.tsx کنار page.tsx، نکست پوسته‌ی صفحه را فوراً می‌فرستد و بقیه را به‌محض آماده‌شدن در همان اتصال جریان می‌دهد.

برای کنترل دقیق‌تر، فقط بخش کند را در Suspense بپیچید تا بقیه‌ی صفحه منتظر نماند:

ts
import { Suspense } from 'react';
import Reviews from './Reviews'; // async Server Component

export default function ProductPage() {
  return (
    <>
      <h1>Product</h1>
      <Suspense fallback={<p>Loading reviews…</p>}>
        <Reviews />
      </Suspense>
    </>
  );
}

این الگو روی شاخص‌هایی مثل LCP اثر مستقیم دارد؛ برای فهم آن‌ها راهنمای Core Web Vitals را ببینید.

داده‌گیری، کش و متادیتا برای سئو

داده‌گیری حالا در خود کامپوننت سروری با async/await انجام می‌شود و دیگر به getServerSideProps و getStaticProps نیاز ندارید. رفتار کش fetch در نسخه‌های مختلف نکست فرق کرده است، پس پیش‌فرض را حدس نزنید و گزینه‌های cache و revalidate را صریح بنویسید.

برای سئو، Metadata API جایگزین next/head شد. عنوان و توضیحات را می‌توان بر اساس داده‌ی همان صفحه ساخت و خروجی در HTML اولیه قرار می‌گیرد، که دقیقاً همان چیزی است که خزنده‌ها می‌خواهند:

ts
// app/blog/[slug]/page.tsx
import type { Metadata } from 'next';

export async function generateMetadata(
  { params }: { params: { slug: string } }
): Promise<Metadata> {
  const post = await getPost(params.slug); // your data function
  return {
    title: post.title,
    description: post.summary,
    alternates: { canonical: `/blog/${params.slug}` }
  };
}

رندر سمت سرور مشکل ایندکس را حل می‌کند، اما بقیه‌ی موارد را فراموش نکنید؛ فهرست کامل را در راهنمای سئوی تکنیکال آورده‌ایم.

مهاجرت از pages به app

نیازی به بازنویسی یک‌باره نیست. هر دو پوشه کنار هم کار می‌کنند، پس می‌توانید مسیر به مسیر جلو بروید. ترتیبی که در پروژه‌های مشتریان جواب داده است:

  1. یک layout ریشه در app/ بسازید و فونت و استایل سراسری را به آن منتقل کنید.
  2. اول صفحه‌های ساده و محتوایی (مقاله، درباره‌ی ما) را منتقل کنید؛ ریسک کم و سود فوری است.
  3. کتابخانه‌هایی که به window یا context وابسته‌اند را در کامپوننت‌های "use client" محصور کنید.
  4. در پایان صفحه‌های پیچیده‌ی داشبورد را با layout تو در تو بازنویسی کنید.

اگر در حال انتخاب راه‌حل برای state سمت کلاینت هستید، مقاله‌ی مدیریت state در ریکت کمک می‌کند، و برای ساختار کامپوننت‌ها اصول کامپوننت‌نویسی را بخوانید. اگر پروژه‌ی بزرگ‌تری دارید، خدمات طراحی و توسعه‌ی وب ما را ببینید.

اشتباه‌های رایج

  • استفاده از useState در کامپوننت سروری و دیدن خطا؛ راه‌حل جداکردن بخش تعاملی به یک کامپوننت کلاینت کوچک است.
  • فرستادن داده‌های غیرقابل‌سریال‌سازی (تابع، نمونه‌ی کلاس) از سرور به کلاینت.
  • فراموش‌کردن error.tsx و loading.tsx و در نتیجه تجربه‌ی کاربری ناقص هنگام خطا یا کندی.
  • فرض‌کردن اینکه کش پیش‌فرض همیشه مطابق انتظار شماست و دیدن داده‌ی کهنه در محیط واقعی.

پرسش‌های متداول

آیا Next.js 13 جایگزین پوشه‌ی pages شده است؟

نه به‌طور کامل. پوشه‌ی app مسیر پیشنهادی برای پروژه‌های جدید است، اما pages همچنان کار می‌کند و می‌توانید هر دو را کنار هم نگه دارید و آهسته مهاجرت کنید.

Server Components چه فرقی با SSR دارند؟

SSR کل کامپوننت را روی سرور به HTML تبدیل می‌کند اما همان کد در مرورگر هم hydrate می‌شود. کامپوننت سروری کدش اصلاً به مرورگر نمی‌رود، پس بسته‌ی جاوااسکریپت کوچک‌تر می‌شود.

کجا باید از use client استفاده کنم؟

فقط در کامپوننت‌هایی که state، افکت، رویداد یا API مرورگر لازم دارند، مثل فرم‌ها، منوهای بازشو و ویجت‌ها. آن را تا حد ممکن به پایین درخت کامپوننت‌ها ببرید.

آیا App Router برای سئو بهتر است؟

به‌خودی‌خود رتبه نمی‌دهد، اما HTML کامل، متادیتای پویا و بارگذاری سریع‌تر مسیر را برای سئو هموار می‌کنند. کیفیت محتوا و ساختار سایت همچنان تعیین‌کننده است.

دیدگاه‌ها

    هنوز دیدگاهی ثبت نشده است. اولین نفر باشید.

    دیدگاه‌ها پس از بررسی منتشر می‌شوند.