021-88881776

آموزش پیشرفته‌سازی در Next.js

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

Fetching داده‌ها

در توسعه وب، دریافت داده‌ها از منابع مختلف یکی از وظایف اساسی است. داده‌ها می‌توانند از API‌های خارجی، پایگاه‌های داده، فایل‌های محلی و غیره تامین شوند. مدیریت بهینه‌ی دریافت و نمایش این داده‌ها تاثیر زیادی بر عملکرد و تجربه کاربری وبسایت شما دارد. پیشرفته‌سازی در Next.js با ارائه ابزارهای متنوع و قدرتمند برای مدیریت دریافت داده‌ها، امکان ایجاد برنامه‌های وب سریع، مقیاس‌پذیر و بهینه را فراهم می‌کند. در این بخش، به بررسی سه روش اصلی دریافت داده در Next.js می‌پردازیم: getStaticProps، getServerSideProps و getStaticPaths.

استفاده از getStaticProps برای داده‌های استاتیک

getStaticProps یکی از توابع پیش‌ساخته در Next.js است که به شما اجازه می‌دهد داده‌ها را در زمان ساخت (build time) دریافت کنید. این روش برای داده‌هایی که به ندرت تغییر می‌کنند و نیاز به بارگذاری سریع دارند مناسب است. با استفاده از این روش، صفحات شما به صورت استاتیک تولید می‌شوند که باعث افزایش سرعت بارگذاری و بهبود سئو می‌شود.

ویژگی‌های getStaticProps:

زمان ساخت: داده‌ها در زمان ساخت دریافت می‌شوند و صفحات به صورت استاتیک ساخته می‌شوند.
بهینه‌سازی سئو: صفحات استاتیک سریع‌تر بارگذاری می‌شوند که به بهبود رتبه‌بندی در موتورهای جستجو کمک می‌کند.
کاهش بار سرور: از آنجا که صفحات به صورت استاتیک تولید می‌شوند، نیاز به پردازش در هر درخواست کاهش می‌یابد.
Incremental Static Regeneration (ISR): با استفاده از گزینه revalidate، می‌توانید به Next.js بگویید که پس از چند ثانیه، صفحه را مجدداً در پس‌زمینه تولید کند تا داده‌ها به‌روز شوند.
مثال:

// pages/blog.js
export async function getStaticProps() {
  const res = await fetch('https://api.example.com/posts');
  const posts = await res.json();

  return {
    props: {
      posts,
    },
    revalidate: 10, // فعال‌سازی ISR برای به‌روزرسانی صفحه هر ۱۰ ثانیه
  };
}

function Blog({ posts }) {
  return (
    <div>
      {posts.map(post => (
        <h2 key={post.id}>{post.title}</h2>
      ))}
    </div>
  );
}

export default Blog;

در این مثال، داده‌ها از API در زمان ساخت دریافت شده و به عنوان props به کامپوننت ارسال می‌شوند. همچنین با استفاده از گزینه revalidate، Next.js هر ۱۰ ثانیه یکبار صفحه را مجدداً تولید می‌کند تا داده‌ها به‌روز شوند بدون نیاز به بازسازی کل سایت.

استفاده از getServerSideProps برای داده‌های سرور-ساید

getServerSideProps برای دریافت داده‌ها در هر درخواست (request) استفاده می‌شود. این روش برای داده‌هایی که به طور مداوم تغییر می‌کنند و نیاز به بروزرسانی دارند، مناسب است. برخلاف getStaticProps که در زمان ساخت اجرا می‌شود، getServerSideProps در هر درخواست جدید اجرا می‌شود و داده‌های تازه را دریافت می‌کند.

ویژگی‌های getServerSideProps:

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

// pages/post/[id].js
export async function getServerSideProps(context) {
  const { id } = context.params;
  const res = await fetch(`https://api.example.com/posts/${id}`);
  const post = await res.json();

  return {
    props: {
      post,
    },
  };
}

function Post({ post }) {
  return <h1>{post.title}</h1>;
}

export default Post;

در اینجا، داده‌ها در هر درخواست جدید از API دریافت می‌شوند و صفحه به صورت سرور-ساید رندر می‌شود. این روش تضمین می‌کند که کاربران همیشه آخرین نسخه از داده‌ها را مشاهده می‌کنند.

استفاده از getStaticPaths برای مسیرهای داینامیک استاتیک

getStaticPaths به همراه getStaticProps برای ایجاد صفحات داینامیک با داده‌های استاتیک استفاده می‌شود. این روش به شما امکان می‌دهد تا صفحات با مسیرهای مختلف را در زمان ساخت تولید کنید. به عنوان مثال، اگر شما یک بلاگ دارید و می‌خواهید برای هر پست یک صفحه جداگانه تولید کنید، از این روش استفاده می‌شود.

ویژگی‌های getStaticPaths:

صفحات داینامیک: امکان تولید صفحات با مسیرهای متفاوت بر اساس داده‌ها.
زمان ساخت: صفحات داینامیک در زمان ساخت تولید می‌شوند.
Fallback: تعیین نحوه رفتار در صورتی که مسیر مورد نظر در زمان ساخت موجود نباشد. می‌توانید از گزینه‌های false، true یا ‘blocking’ استفاده کنید.
پشتیبانی از ISR برای صفحات داینامیک: با تنظیم revalidate در getStaticProps، می‌توانید صفحات داینامیک را نیز به‌روز کنید.
مثال:

// pages/posts/[id].js
export async function getStaticPaths() {
  const res = await fetch('https://api.example.com/posts');
  const posts = await res.json();

  const paths = posts.map(post => ({
    params: { id: post.id.toString() },
  }));

  return { paths, fallback: false };
}

export async function getStaticProps({ params }) {
  const res = await fetch(`https://api.example.com/posts/${params.id}`);
  const post = await res.json();

  return {
    props: {
      post,
    },
    revalidate: 10, // فعال‌سازی ISR برای به‌روزرسانی صفحه هر ۱۰ ثانیه
  };
}

function Post({ post }) {
  return <h1>{post.title}</h1>;
}

export default Post;

در این مثال، برای هر پست یک مسیر استاتیک ایجاد می‌شود که در زمان ساخت تولید می‌گردد. با تنظیم fallback: false، فقط مسیرهای مشخص شده در paths تولید می‌شوند و هر مسیر دیگری منجر به خطای 404 می‌شود. اگر می‌خواهید مسیرهای جدیدی اضافه کنید بدون نیاز به بازسازی کل سایت، می‌توانید از fallback: true یا fallback: ‘blocking’ استفاده کنید. همچنین با استفاده از revalidate، می‌توانید صفحات داینامیک را به‌روز کنید بدون نیاز به بازسازی کل سایت.

تفاوت‌ها و کاربردهای هر روش

هر یک از روش‌های دریافت داده در Next.js دارای ویژگی‌ها و کاربردهای خاص خود هستند. انتخاب مناسب‌ترین روش بستگی به نیازهای پروژه شما دارد. در زیر به بررسی تفاوت‌ها و کاربردهای هر روش می‌پردازیم:

getStaticProps:

زمان اجرا: زمان ساخت (Build Time)
داده‌های مناسب: داده‌های استاتیک که به ندرت تغییر می‌کنند
سرعت بارگذاری: بسیار سریع (صفحات استاتیک)
به‌روزرسانی داده‌ها: نیاز به بازسازی سایت یا استفاده از ISR برای به‌روزرسانی جزئی
کاربردهای رایج: بلاگ‌ها، صفحات محصول با اطلاعات ثابت

getServerSideProps:

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

getStaticPaths:

زمان اجرا: زمان ساخت (Build Time)
داده‌های مناسب: تولید صفحات داینامیک با داده‌های استاتیک
سرعت بارگذاری: بسیار سریع (صفحات استاتیک)
به‌روزرسانی داده‌ها: با استفاده از ISR می‌توان به‌روزرسانی جزئی داشت
کاربردهای رایج: صفحات محصول داینامیک، پست‌های بلاگ

در پیشرفته‌سازی در Next.js، انتخاب صحیح بین getStaticProps، getServerSideProps و getStaticPaths می‌تواند تأثیر زیادی بر عملکرد و تجربه کاربری وبسایت شما داشته باشد. برای داده‌های استاتیک و صفحات با اطلاعات ثابت، استفاده از getStaticProps بهترین گزینه است. برای داده‌های پویا و صفحات نیازمند به‌روزرسانی لحظه‌ای، getServerSideProps مناسب‌تر است. و برای تولید صفحات داینامیک با داده‌های استاتیک، ترکیب getStaticPaths با getStaticProps بهترین انتخاب می‌باشد. با درک دقیق از نیازهای پروژه و ویژگی‌های هر روش، می‌توانید بهترین تصمیم را برای مدیریت دریافت داده‌ها در پروژه‌های Next.js خود بگیرید.

بهینه‌سازی تصاویر

تصاویر بخش مهمی از تجربه کاربری وب هستند و بهینه‌سازی آن‌ها می‌تواند تأثیر زیادی بر سرعت بارگذاری و عملکرد سایت داشته باشد. در پیشرفته‌سازی در Next.js، بهینه‌سازی تصاویر با استفاده از ابزارها و تکنیک‌های مختلف امکان‌پذیر است. در این بخش، به بررسی کامپوننت Image در Next.js و تنظیمات مختلف آن برای بهینه‌سازی تصاویر می‌پردازیم.

استفاده از کامپوننت Image در Next.js

Next.js کامپوننت Image را ارائه می‌دهد که به صورت خودکار تصاویر را بهینه‌سازی می‌کند. این کامپوننت امکاناتی مانند لندینگ تنبل (lazy loading)، تغییر اندازه و انتخاب فرمت مناسب را فراهم می‌کند. استفاده از کامپوننت Image نه تنها به بهبود عملکرد وبسایت کمک می‌کند، بلکه تجربه کاربری بهتری نیز فراهم می‌آورد.

ویژگی‌های اصلی کامپوننت Image:

لندینگ تنبل (Lazy Loading): تصاویر فقط زمانی بارگذاری می‌شوند که در دید کاربر قرار بگیرند، که این امر باعث کاهش زمان بارگذاری اولیه صفحه می‌شود.
تغییر اندازه (Resizing): امکان تنظیم ابعاد دقیق تصاویر به کاهش حجم فایل و بهبود سرعت بارگذاری.
انتخاب فرمت مناسب (Format Selection): بهینه‌سازی خودکار تصاویر با انتخاب فرمت مناسب مانند WebP برای کاهش حجم بدون کاهش کیفیت.
پشتیبانی از تصاویر ریسپانسیو: بهینه‌سازی تصاویر برای نمایش در دستگاه‌های مختلف با استفاده از ویژگی‌های sizes و layout.
مثال:

import Image from 'next/image';

function Profile() {
  return (
    <div>
      <Image
        src="/me.png"
        alt="Picture of the author"
        width={500}
        height={500}
      />
    </div>
  );
}

export default Profile;

در این مثال، با استفاده از کامپوننت Image، تصویر به صورت خودکار بهینه‌سازی شده و اندازه‌های مشخص شده را دارد. این امر باعث می‌شود که تصویر سریع‌تر بارگذاری شود و تجربه کاربری بهتری ارائه دهد.

تنظیمات بهینه‌سازی تصاویر (لندینگ، سایز، فرمت)

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

لندینگ تنبل (Lazy Loading)

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

مثال:

<Image
  src="/lazy-image.jpg"
  alt="Lazy Loaded Image"
  width={800}
  height={600}
  loading="lazy"
/>

در این مثال، ویژگی loading=”lazy” باعث می‌شود که تصویر تنها زمانی بارگذاری شود که در دید کاربر قرار گیرد.

تغییر اندازه (Resizing)

تغییر اندازه تصاویر به شما اجازه می‌دهد تا ابعاد دقیق تصاویر را تنظیم کنید. این کار به کاهش حجم فایل‌ها و بهبود سرعت بارگذاری کمک می‌کند.

مثال:

<Image
  src="/large-image.jpg"
  alt="Resized Image"
  width={400}
  height={300}
/>

در این مثال، تصویر با ابعاد ۴۰۰ در ۳۰۰ پیکسل بارگذاری می‌شود، که حجم آن را نسبت به نسخه اصلی کاهش می‌دهد.

فرمت (Format)

انتخاب فرمت مناسب برای تصاویر می‌تواند تاثیر زیادی بر حجم فایل داشته باشد. فرمت‌های مدرن مانند WebP می‌توانند حجم تصاویر را بدون کاهش کیفیت کاهش دهند.

مثال:

<Image
  src="/image.webp"
  alt="WebP Image"
  width={500}
  height={500}
/>

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

پشتیبانی از تصاویر ریسپانسیو

کامپوننت Image به طور خودکار از تصاویر ریسپانسیو پشتیبانی می‌کند. این به معنای این است که تصاویر به طور مناسب در اندازه‌های مختلف نمایش داده می‌شوند و برای دستگاه‌های مختلف بهینه شده‌اند. با استفاده از ویژگی‌های sizes و layout می‌توانید تصاویر را برای دستگاه‌های مختلف بهینه کنید.

ویژگی‌های مهم برای تصاویر ریسپانسیو:

sizes: تعیین می‌کند که تصویر در چه اندازه‌ای نمایش داده شود.
layout: تعیین نحوه قرارگیری و اندازه‌گیری تصویر (مثلاً responsive, fixed, intrinsic).
مثال:

<Image
  src="/responsive-image.jpg"
  alt="Responsive Image"
  layout="responsive"
  width={700}
  height={475}
/>

در این مثال، با تنظیم layout=”responsive”, تصویر به طور خودکار برای اندازه‌های مختلف دستگاه‌ها بهینه می‌شود. ویژگی‌های width و height نیز به تعیین ابعاد اصلی تصویر کمک می‌کنند.

مزایای تصاویر ریسپانسیو:

بهبود تجربه کاربری: تصاویر به طور بهینه در اندازه‌های مختلف نمایش داده می‌شوند و از بزرگنمایی یا کوچک‌نمایی غیرضروری جلوگیری می‌شود.
کاهش مصرف پهنای باند: تصاویر تنها با اندازه مناسب برای دستگاه کاربر بارگذاری می‌شوند.
افزایش سرعت بارگذاری: بهینه‌سازی تصاویر برای هر دستگاه باعث کاهش زمان بارگذاری صفحات می‌شود.

نکات پیشرفته برای بهینه‌سازی تصاویر در Next.js

برای بهره‌برداری کامل از امکانات بهینه‌سازی تصاویر در پیشرفته‌سازی در Next.js، می‌توانید از تکنیک‌های زیر استفاده کنید:

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

مثال پیش‌بارگذاری:

<head>
  <link rel="preload" href="/critical-image.jpg" as="image" />
</head>

این کد باعث می‌شود که تصویر critical-image.jpg در زمان بارگذاری صفحه به صورت پیش‌بارگذاری شود و سریع‌تر نمایش داده شود.

بهینه‌سازی تصاویر یکی از جنبه‌های حیاتی در پیشرفته‌سازی در Next.js است که می‌تواند تأثیر زیادی بر سرعت بارگذاری و تجربه کاربری وبسایت شما داشته باشد. با استفاده از کامپوننت Image و تنظیمات مختلف آن مانند لندینگ تنبل، تغییر اندازه، انتخاب فرمت مناسب و پشتیبانی از تصاویر ریسپانسیو، می‌توانید تصاویر را به نحوی بهینه کنید که هم کیفیت مناسب داشته باشند و هم حجم فایل‌ها کم باشد. همچنین، با بهره‌گیری از تکنیک‌های پیشرفته‌تر مانند استفاده از CDN و فشرده‌سازی تصاویر، می‌توانید عملکرد وبسایت خود را به حداکثر برسانید.

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

داده‌های کلاینت-ساید

مدیریت داده‌ها در سمت کلاینت یکی از چالش‌های اصلی در توسعه وب است. در پیشرفته‌سازی در Next.js، بهینه‌سازی و مدیریت موثر داده‌های کلاینت-ساید می‌تواند تأثیر قابل توجهی بر عملکرد و تجربه کاربری وبسایت داشته باشد. Next.js ابزارها و کتابخانه‌های متعددی را برای این منظور فراهم می‌کند که در این بخش به بررسی آن‌ها می‌پردازیم. به ویژه، به بررسی استفاده از SWR و React Query برای فچینگ و مدیریت داده‌ها می‌پردازیم و نحوه کشینگ و بهینه‌سازی درخواست‌ها را مورد بحث قرار می‌دهیم.

استفاده از SWR برای فچینگ داده‌ها

SWR یک کتابخانه سبک و کارآمد برای فچینگ داده‌ها در React است که به طور طبیعی با Next.js سازگار است. نام SWR از حروف اول کلمات Stale-While-Revalidate گرفته شده است که استراتژی کشینگ این کتابخانه را توصیف می‌کند. SWR به شما امکان می‌دهد داده‌ها را به صورت بهینه و با کمترین تلاش مدیریت کنید.

ویژگی‌های اصلی SWR:

کشینگ هوشمند: SWR به طور خودکار داده‌ها را کش می‌کند و از کش برای بهبود سرعت بارگذاری استفاده می‌کند.
رفرش خودکار: داده‌ها به طور منظم به‌روزرسانی می‌شوند تا همیشه اطلاعات تازه در اختیار کاربران قرار گیرد.
مدیریت خطا: امکان مدیریت ساده و موثر خطاها را فراهم می‌کند.
استفاده آسان: با یک API ساده و قابل فهم می‌توانید به سرعت داده‌ها را فچ کنید و نمایش دهید.
مثال:

import useSWR from 'swr';

// تعریف fetcher برای فچینگ داده‌ها
const fetcher = url => fetch(url).then(res => res.json());

function Profile() {
  const { data, error } = useSWR('/api/user', fetcher);

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

  return <div>سلام، {data.name}!</div>;
}

export default Profile;

در این مثال، کامپوننت Profile از useSWR برای فچینگ داده‌های کاربر از API استفاده می‌کند. SWR به طور خودکار داده‌ها را کش می‌کند و در صورت بروز تغییرات، آن‌ها را به‌روزرسانی می‌کند. این روش نه تنها کد را ساده‌تر می‌کند، بلکه عملکرد وبسایت را نیز بهبود می‌بخشد.

مدیریت داده‌های کلاینت-ساید با React Query

React Query یک کتابخانه قدرتمند برای مدیریت داده‌های کلاینت-ساید است که امکانات پیشرفته‌ای مانند کشینگ پیشرفته، هماهنگی داده‌ها و رفرش خودکار را فراهم می‌کند. این کتابخانه به شما کمک می‌کند تا داده‌های خود را به صورت بهینه و کارآمد مدیریت کنید و از پیچیدگی‌های مرتبط با فچینگ و کشینگ داده‌ها جلوگیری کنید.

ویژگی‌های اصلی React Query:

کشینگ پیشرفته: React Query امکان کش کردن داده‌ها را به صورت هوشمند فراهم می‌کند و از کش برای بهبود سرعت بارگذاری استفاده می‌کند.
هماهنگی داده‌ها: این کتابخانه به شما امکان می‌دهد تا داده‌ها را در بین چندین کامپوننت به صورت هماهنگ مدیریت کنید.
رفرش خودکار: داده‌ها به طور منظم به‌روزرسانی می‌شوند تا همیشه اطلاعات تازه در اختیار کاربران قرار گیرد.
پشتیبانی از Pagination و Infinite Queries: مدیریت ساده برای بارگذاری داده‌های صفحه‌بندی شده یا بی‌نهایت.
مثال:

import { useQuery } from 'react-query';

function Posts() {
  const { data, error, isLoading } = useQuery('posts', () =>
    fetch('/api/posts').then(res => res.json())
  );

  if (isLoading) return <div>در حال بارگذاری...</div>;
  if (error) return <div>خطا در بارگذاری</div>;

  return (
    <ul>
      {data.map(post => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  );
}

export default Posts;

در این مثال، کامپوننت Posts از useQuery برای فچینگ داده‌های پست‌ها استفاده می‌کند. React Query به طور خودکار داده‌ها را کش می‌کند و در صورت تغییر داده‌ها، آن‌ها را به‌روزرسانی می‌کند. همچنین، مدیریت وضعیت‌های بارگذاری و خطا به صورت ساده و کارآمد انجام می‌شود.

کشینگ و بهینه‌سازی درخواست‌ها

استفاده از کتابخانه‌هایی مانند SWR و React Query به بهینه‌سازی درخواست‌ها و کاهش بار سرور کمک می‌کند. این کتابخانه‌ها با کش کردن داده‌ها، از ارسال درخواست‌های مکرر جلوگیری می‌کنند و تجربه کاربری بهتری را فراهم می‌کنند. در ادامه به بررسی نحوه کشینگ و بهینه‌سازی درخواست‌ها با استفاده از این کتابخانه‌ها می‌پردازیم.

کشینگ هوشمند

کشینگ داده‌ها به شما امکان می‌دهد تا داده‌های فچ شده را در حافظه موقت نگه دارید و از آن‌ها برای نمایش سریع‌تر در درخواست‌های بعدی استفاده کنید. این کار باعث کاهش تعداد درخواست‌های HTTP به سرور می‌شود و زمان بارگذاری را کاهش می‌دهد.

مثال با SWR:

import useSWR from 'swr';

const fetcher = url => fetch(url).then(res => res.json());

function Dashboard() {
  const { data, error } = useSWR('/api/dashboard', fetcher, {
    dedupingInterval: 60000, // جلوگیری از ارسال درخواست‌های مکرر در مدت زمان مشخص
  });

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

  return <div>اطلاعات داشبورد: {data.info}</div>;
}

export default Dashboard;

در این مثال، با استفاده از گزینه dedupingInterval، از ارسال درخواست‌های مکرر به سرور در بازه زمانی مشخص جلوگیری می‌شود.

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

بهینه‌سازی درخواست‌ها به معنای کاهش تعداد و حجم درخواست‌های HTTP است که به بهبود عملکرد وبسایت کمک می‌کند. با استفاده از SWR و React Query، می‌توانید درخواست‌ها را به صورت بهینه مدیریت کنید.

مثال با React Query:

import { useQuery } from 'react-query';

function UserProfile() {
  const { data, error, isLoading } = useQuery(
    ['user', 'profile'],
    () => fetch('/api/user/profile').then(res => res.json()),
    {
      staleTime: 300000, // داده‌ها برای ۵ دقیقه تازه محسوب می‌شوند
      cacheTime: 600000, // داده‌ها برای ۱۰ دقیقه در کش نگه‌داری می‌شوند
    }
  );

  if (isLoading) return <div>در حال بارگذاری...</div>;
  if (error) return <div>خطا در بارگذاری</div>;

  return <div>نام کاربر: {data.name}</div>;
}

export default UserProfile;

در این مثال، با استفاده از گزینه‌های staleTime و cacheTime، می‌توانید تعیین کنید که داده‌ها چقدر تازه باقی می‌مانند و چه مدت در کش نگه‌داری شوند. این تنظیمات به بهینه‌سازی درخواست‌ها و کاهش بار سرور کمک می‌کند.  در پیشرفته‌سازی در Next.js، مدیریت داده‌های کلاینت-ساید با استفاده از کتابخانه‌هایی مانند SWR و React Query می‌تواند عملکرد و تجربه کاربری وبسایت شما را به طور قابل توجهی بهبود بخشد. این ابزارها با ارائه امکانات پیشرفته‌ای مانند کشینگ هوشمند، رفرش خودکار و مدیریت خطا، به توسعه‌دهندگان کمک می‌کنند تا داده‌ها را به صورت بهینه و کارآمد مدیریت کنند. علاوه بر این، بهینه‌سازی درخواست‌ها با استفاده از این کتابخانه‌ها باعث کاهش بار سرور و افزایش سرعت بارگذاری صفحات می‌شود.

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

استفاده از TypeScript در Next.js

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

راه‌اندازی TypeScript در پروژه Next.js

برای استفاده از TypeScript در Next.js، کافی است TypeScript و نوع‌های مربوطه را نصب کنید و فایل‌های پروژه را با پسوند .ts و .tsx ایجاد کنید. این فرآیند به سادگی قابل انجام است و چند مرحله اصلی دارد که در ادامه به آن‌ها می‌پردازیم.

مراحل:

نصب TypeScript و نوع‌های مورد نیاز:

ابتدا باید TypeScript و نوع‌های مربوط به React و Node.js را به عنوان وابستگی‌های توسعه (development dependencies) نصب کنید. این کار را با اجرای دستور زیر در ترمینال انجام دهید:

npm install --save-dev typescript @types/react @types/node

یا اگر از Yarn استفاده می‌کنید:

yarn add --dev typescript @types/react @types/node

ایجاد فایل tsconfig.json:

پس از نصب TypeScript، باید فایل تنظیمات TypeScript (tsconfig.json) را ایجاد کنید. این فایل تنظیمات مربوط به کامپایلر TypeScript را در پروژه مشخص می‌کند. با اجرای دستور زیر می‌توانید فایل tsconfig.json را به صورت خودکار ایجاد کنید:

npx tsc --init

این دستور یک فایل تنظیمات پایه‌ای ایجاد می‌کند که می‌توانید آن را به نیازهای پروژه خود سفارشی‌سازی کنید.

تغییر پسوند فایل‌های جاوااسکریپت به .ts و .tsx:

پس از نصب TypeScript و ایجاد فایل تنظیمات، باید پسوند فایل‌های جاوااسکریپت پروژه را به .ts و .tsx تغییر دهید. فایل‌های کامپوننت‌ها و صفحات معمولاً از پسوند .tsx استفاده می‌کنند که امکان استفاده از JSX را فراهم می‌کند، در حالی که فایل‌های معمولی جاوااسکریپت می‌توانند از پسوند .ts استفاده کنند.

مثال:

تغییر pages/index.js به pages/index.tsx
تغییر components/Header.js به components/Header.tsx
تغییر utils/helpers.js به utils/helpers.ts

تنظیمات پیشرفته TypeScript در Next.js

پس از راه‌اندازی اولیه، می‌توانید تنظیمات پیشرفته‌تری را در فایل tsconfig.json اعمال کنید تا بهره‌وری بیشتری از TypeScript در پروژه خود ببرید.

مثال فایل tsconfig.json:

{
  "compilerOptions": {
    "target": "es5",
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "skipLibCheck": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "noEmit": true,
    "esModuleInterop": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "jsx": "preserve",
    "incremental": true
  },
  "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
  "exclude": ["node_modules"]
}

توضیحات مهم:

strict: فعال‌سازی قوانین سخت‌گیرانه‌تر TypeScript که باعث کاهش خطاهای احتمالی می‌شود.
noEmit: جلوگیری از تولید فایل‌های جاوااسکریپت در هنگام کامپایل TypeScript.
esModuleInterop: فراهم کردن سازگاری بهتر با ماژول‌های CommonJS.
incremental: بهبود سرعت کامپایل با استفاده از کامپایل‌های افزایشی.

تعریف انواع (Types) و اینترفیس‌ها

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

انواع (Types) و اینترفیس‌ها

در TypeScript، می‌توانید از type و interface برای تعریف انواع استفاده کنید. هر دوی آن‌ها قابلیت‌های مشابهی دارند اما تفاوت‌های جزئی نیز دارند.

مثال:

// تعریف نوع با استفاده از interface
interface Post {
  id: number;
  title: string;
  content: string;
}

// تعریف نوع با استفاده از type
type User = {
  id: number;
  name: string;
  email: string;
};

استفاده از انواع در کامپوننت‌ها

تعریف انواع برای پروپس (props) کامپوننت‌ها باعث می‌شود که کامپوننت‌ها به صورت ایمن‌تر و قابل‌اعتمادتر عمل کنند.

مثال:

// components/PostList.tsx
import React from 'react';

interface Post {
  id: number;
  title: string;
  content: string;
}

interface Props {
  posts: Post[];
}

const PostList: React.FC<Props> = ({ posts }) => {
  return (
    <div>
      {posts.map(post => (
        <div key={post.id}>
          <h2>{post.title}</h2>
          <p>{post.content}</p>
        </div>
      ))}
    </div>
  );
};

export default PostList;

در این مثال، کامپوننت PostList انواع پروپس خود را با استفاده از اینترفیس Props تعریف کرده است که شامل یک آرایه از پست‌ها می‌باشد. این امر باعث می‌شود که هر گونه تغییر نامناسب در داده‌ها توسط TypeScript شناسایی و گزارش شود.

استفاده از انواع در توابع

TypeScript به شما امکان می‌دهد تا نوع پارامترها و نوع بازگشتی توابع را مشخص کنید که به بهبود خوانایی و قابلیت نگهداری کد کمک می‌کند.

مثال:

// utils/helpers.ts
export interface User {
  id: number;
  name: string;
  email: string;
}

export const getUserFullName = (user: User): string => {
  return `${user.name}`;
};

در این مثال، تابع getUserFullName نوع پارامتر user و نوع بازگشتی آن را به صورت دقیق تعریف کرده است.

استفاده از TypeScript با API Routes و کامپوننت‌ها

TypeScript می‌تواند برای تایپ کردن API Routes و کامپوننت‌ها در Next.js استفاده شود که به افزایش دقت و کاهش خطاها کمک می‌کند. با تایپ کردن API Routes، می‌توانید اطمینان حاصل کنید که داده‌های ارسالی و دریافتی با ساختار مورد انتظار مطابقت دارند.

مثال API Route با TypeScript

// pages/api/posts.ts
import { NextApiRequest, NextApiResponse } from 'next';

interface Post {
  id: number;
  title: string;
  content: string;
}

const posts: Post[] = [
  { id: 1, title: 'پست اول', content: 'محتوای پست اول' },
  { id: 2, title: 'پست دوم', content: 'محتوای پست دوم' },
  // ... سایر پست‌ها
];

export default function handler(req: NextApiRequest, res: NextApiResponse<Post[]>) {
  if (req.method === 'GET') {
    res.status(200).json(posts);
  } else {
    res.setHeader('Allow', ['GET']);
    res.status(405).end(`Method ${req.method} Not Allowed`);
  }
}

در این مثال، API Route برای دریافت لیست پست‌ها با استفاده از TypeScript تایپ شده است. این تایپ‌گذاری باعث می‌شود تا هر گونه تغییر ناخواسته در ساختار داده‌ها به سرعت شناسایی شود.

تایپ کردن کامپوننت‌های API Route

می‌توانید داده‌های ورودی و خروجی API Route‌ها را به صورت دقیق تایپ کنید تا از ورود داده‌های نامعتبر جلوگیری شود.

مثال:

// pages/api/user.ts
import { NextApiRequest, NextApiResponse } from 'next';

interface User {
  id: number;
  name: string;
  email: string;
}

const user: User = {
  id: 1,
  name: 'علی',
  email: 'ali@example.com',
};

export default function handler(req: NextApiRequest, res: NextApiResponse<User>) {
  if (req.method === 'GET') {
    res.status(200).json(user);
  } else {
    res.setHeader('Allow', ['GET']);
    res.status(405).end(`Method ${req.method} Not Allowed`);
  }
}

در اینجا، API Route برای دریافت اطلاعات کاربر با استفاده از TypeScript تایپ شده است که تضمین می‌کند داده‌های خروجی با ساختار تعریف شده مطابقت دارند.

تایپ کردن کامپوننت‌ها با استفاده از Generics

TypeScript به شما اجازه می‌دهد تا از Generics برای ایجاد کامپوننت‌های انعطاف‌پذیر و قابل‌استفاده مجدد استفاده کنید.

مثال:

// components/DataList.tsx
import React from 'react';

interface DataListProps<T> {
  data: T[];
  renderItem: (item: T) => React.ReactNode;
}

const DataList = <T,>({ data, renderItem }: DataListProps<T>) => {
  return <div>{data.map(renderItem)}</div>;
};

export default DataList;

در این مثال، کامپوننت DataList از Generics استفاده می‌کند تا بتواند با هر نوع داده‌ای کار کند. این روش باعث می‌شود که کامپوننت‌ها انعطاف‌پذیرتر و قابل استفاده در موقعیت‌های مختلف باشند.

استفاده از TypeScript در صفحات و مسیرهای داینامیک

در پروژه‌های Next.js با صفحات و مسیرهای داینامیک، استفاده از TypeScript به شما کمک می‌کند تا اطمینان حاصل کنید که پارامترهای مسیر و داده‌های دریافت شده با ساختار مورد انتظار مطابقت دارند.

مثال:

// pages/posts/[id].tsx
import { GetStaticPaths, GetStaticProps } from 'next';
import { useRouter } from 'next/router';

interface Post {
  id: number;
  title: string;
  content: string;
}

interface PostProps {
  post: Post;
}

const PostPage: React.FC<PostProps> = ({ post }) => {
  const router = useRouter();

  // نمایش وضعیت بارگذاری در صورتی که fallback فعال باشد
  if (router.isFallback) {
    return <div>در حال بارگذاری...</div>;
  }

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

export const getStaticPaths: GetStaticPaths = async () => {
  const res = await fetch('https://api.example.com/posts');
  const posts: Post[] = await res.json();

  const paths = posts.map(post => ({
    params: { id: post.id.toString() },
  }));

  return { paths, fallback: false };
};

export const getStaticProps: GetStaticProps<PostProps> = async ({ params }) => {
  const res = await fetch(`https://api.example.com/posts/${params?.id}`);
  const post: Post = await res.json();

  return {
    props: {
      post,
    },
    revalidate: 10, // فعال‌سازی ISR
  };
};

export default PostPage;

در این مثال، صفحه پست‌های داینامیک با استفاده از TypeScript تایپ شده است. توابع getStaticPaths و getStaticProps نیز با انواع مشخص شده عمل می‌کنند که باعث افزایش دقت و کاهش خطاها می‌شود.

استفاده از TypeScript با کتابخانه‌های مدیریت حالت (State Management)

استفاده از TypeScript در ترکیب با کتابخانه‌های مدیریت حالت مانند Redux یا Zustand می‌تواند به بهبود قابلیت نگهداری و افزایش ایمنی کدها کمک کند.

مثال با Redux:

تعریف نوع‌های State و Action:

// store/types.ts
export interface CounterState {
  count: number;
}

export interface IncrementAction {
  type: 'INCREMENT';
}

export interface DecrementAction {
  type: 'DECREMENT';
}

export type CounterAction = IncrementAction | DecrementAction;

ایجاد Reducer با TypeScript:

// store/reducer.ts
import { CounterState, CounterAction } from './types';

const initialState: CounterState = {
  count: 0,
};

const counterReducer = (state = initialState, action: CounterAction): CounterState => {
  switch (action.type) {
    case 'INCREMENT':
      return { count: state.count + 1 };
    case 'DECREMENT':
      return { count: state.count - 1 };
    default:
      return state;
  }
};

export default counterReducer;

ایجاد Store با TypeScript:

// store/store.ts
import { createStore } from 'redux';
import counterReducer from './reducer';

const store = createStore(counterReducer);

export default store;

استفاده از Store در کامپوننت‌ها:

// components/Counter.tsx
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { RootState } from '../store/types';

const Counter: React.FC = () => {
  const count = useSelector((state: RootState) => state.count);
  const dispatch = useDispatch();

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => dispatch({ type: 'INCREMENT' })}>Increment</button>
      <button onClick={() => dispatch({ type: 'DECREMENT' })}>Decrement</button>
    </div>
  );
};

export default Counter;

در این مثال، با استفاده از TypeScript، نوع‌های state و action به طور دقیق تعریف شده‌اند که باعث می‌شود استفاده از Redux ایمن‌تر و قابل‌اعتمادتر شود.

نکات پیشرفته برای استفاده از TypeScript در Next.js

برای بهره‌برداری کامل از امکانات TypeScript در پیشرفته‌سازی در Next.js، می‌توانید از تکنیک‌ها و ابزارهای پیشرفته زیر استفاده کنید:

استفاده از Generics در کامپوننت‌ها و توابع:

Generics به شما اجازه می‌دهد تا کامپوننت‌ها و توابعی بنویسید که با انواع مختلف داده کار کنند بدون نیاز به تکرار کد.

مثال:

// components/GenericList.tsx
import React from 'react';

interface GenericListProps<T> {
  items: T[];
  renderItem: (item: T) => React.ReactNode;
}

const GenericList = <T,>({ items, renderItem }: GenericListProps<T>) => {
  return <ul>{items.map(renderItem)}</ul>;
};

export default GenericList;

استفاده از Utility Types:

TypeScript انواع ابزاری مانند Partial, Pick, Omit و غیره را فراهم می‌کند که به شما امکان می‌دهد انواع موجود را تغییر دهید یا ترکیب کنید.

مثال:

interface User {
  id: number;
  name: string;
  email: string;
  age?: number;
}

// ایجاد نوع جدید با ویژگی‌های انتخاب شده
type UserPreview = Pick<User, 'id' | 'name'>;

// استفاده از نوع جدید در کامپوننت
const UserCard: React.FC<{ user: UserPreview }> = ({ user }) => (
  <div>
    <h2>{user.name}</h2>
    <p>ID: {user.id}</p>
  </div>
);

ایجاد تایپ‌های سفارشی با Mapped Types:

Mapped Types به شما امکان می‌دهند تا انواع جدیدی را بر اساس انواع موجود ایجاد کنید.

مثال:

type Readonly<T> = {
  readonly [P in keyof T]: T[P];
};

interface Product {
  id: number;
  name: string;
  price: number;
}

type ReadonlyProduct = Readonly<Product>;

const product: ReadonlyProduct = {
  id: 1,
  name: 'Laptop',
  price: 1500,
};

// تلاش برای تغییر مقدار باعث خطا می‌شود
// product.price = 1600; // Error: Cannot assign to 'price' because it is a read-only property.

استفاده از Type Guards:

Type Guards به شما امکان می‌دهند تا انواع مختلف را در کدهای خود شناسایی و مدیریت کنید.

مثال:

interface Cat {
  name: string;
  meow(): void;
}

interface Dog {
  name: string;
  bark(): void;
}

type Pet = Cat | Dog;

function isCat(pet: Pet): pet is Cat {
  return (pet as Cat).meow !== undefined;
}

function speak(pet: Pet) {
  if (isCat(pet)) {
    pet.meow();
  } else {
    pet.bark();
  }
}

استفاده از TypeScript در تست‌ها:

تایپ‌گذاری دقیق می‌تواند به بهبود کیفیت تست‌ها کمک کند و از وارد شدن داده‌های نامعتبر جلوگیری کند.

مثال با Jest:

// __tests__/sum.test.ts
import { sum } from '../utils/sum';

test('adds 1 + 2 to equal 3', () => {
  expect(sum(1, 2)).toBe(3);
});

استفاده از TypeScript در Next.js یکی از بهترین روش‌ها برای افزایش کیفیت و قابلیت نگهداری پروژه‌های وب است. با تعریف انواع دقیق برای داده‌ها، کامپوننت‌ها و API Routes، می‌توانید از بروز خطاهای رایج جلوگیری کنید و کدهای قابل‌اعتمادتر و خواناتری بنویسید. در پیشرفته‌سازی در Next.js، ترکیب TypeScript با ابزارها و کتابخانه‌های مدرن مانند SWR و React Query می‌تواند به بهبود عملکرد و تجربه کاربری وبسایت شما کمک کند.

با بهره‌گیری از تکنیک‌های پیشرفته TypeScript مانند Generics، Utility Types و Type Guards، می‌توانید کدهای پیچیده‌تر و قابل‌اعتمادتری ایجاد کنید که به کاهش زمان توسعه و افزایش کارایی تیم شما کمک می‌کند. همچنین، استفاده از TypeScript در تست‌ها و مدیریت حالت با کتابخانه‌های مدیریت حالت، به شما امکان می‌دهد تا پروژه‌های بزرگ‌تر و مقیاس‌پذیرتری را با اطمینان بیشتری توسعه دهید.

نتیجه‌گیری

در این مقاله به پیشرفته‌سازی در Next.js پرداختیم و ابزارها و تکنیک‌های مهمی مانند getStaticProps، getServerSideProps، getStaticPaths، کامپوننت Image، کتابخانه‌های SWR و React Query، و استفاده از TypeScript را مورد بررسی قرار دادیم. این روش‌ها به بهبود عملکرد، سرعت بارگذاری، و تجربه کاربری وبسایت‌ها کمک شایانی می‌کنند. همچنین، با استفاده از TypeScript، قابلیت نگهداری و کاهش خطاها در پروژه‌های Next.js افزایش یافته است.

پیشرفته‌سازی در Next.js به توسعه‌دهندگان امکان می‌دهد وبسایت‌هایی سریع‌تر، بهینه‌تر و قابل‌اعتمادتر بسازند. با بهره‌گیری از تکنیک‌ها و ابزارهای معرفی شده، می‌توانید پروژه‌های پیچیده‌تر و مقیاس‌پذیرتری را با کیفیت بالاتر پیاده‌سازی کنید.

آموزش پیشرفته‌سازی در Next.js

دیدگاه های شما

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *