كثير من النصائح الشائعة عن أداء Next.js عامة أكثر من اللازم. هذا الدليل يركّز على التحسينات التي يمكنك قياسها فعلياً، فترى الفرق في رقم حقيقي قبل وبعد التغيير.

قِس أولاً ثم حسّن

قبل أي تعديل، سجّل خط الأساس. افحص حجم الحزمة، وقس أرقام الأداء الأساسية من Lighthouse في وضع التصفح الخاص لتفادي الكاش. بدون خط أساس لا تستطيع إثبات أن تحسينك فعل شيئاً.

أوامر القياسbash
# حجم الحزمة
npx @next/bundle-analyzer

# قياس الأداء
npx lighthouse http://localhost:3000 --view

اجعل المكونات تعمل على الخادم ما لم تحتج متصفحاً

أكبر مكسب في تقليل JavaScript هو عدم إرساله أصلاً. المكوّن الذي يعمل على الخادم ينفّذ هناك ولا يصل إلى المتصفح إلا الناتج النهائي. أضف توجيه_use client في أقل عدد ممكن من المواضع، ولا تضعه في ملف مشترك عالٍ في الشجرة.

هيكل مثاليtsx
// مكوّن خادم، بدون توجيه_use client
export default async function Page() {
  const data = await getData();

  return (
    <div>
      <StaticInfo items={data} />
      <InteractiveChart data={data} />
    </div>
  );
}

حمّل المكتبات الثقيلة عند الحاجة

محرر أكواد كبير أو مكتبة تواريخ ثقيلة أو أي مخطط بياني ضخم يجب ألا يدخل الحزمة الأساسية إن لم يكن مطلوباً في أول رسم. استخدم التحميل الكسول مع عنصر بديل أثناء الانتظار:

lazy-chart.tsxtsx
import dynamic from "next/dynamic";

const Chart = dynamic(() => import("./Chart"), {
  ssr: false,
  loading: () => <div className="h-64 animate-pulse rounded-xl" />
});

export function ChartSlot({ data }: { data: number[] }) {
  return <Chart data={data} />;
}

الصور: استخدم المكوّن المخصص وصيغاً حديثة

الصور غالباً أكبر مساهم في بطء أول ظهور للمحتوى. المكوّن المخصص في Next.js يضيف الأبعاد المطلوبة، ويحمّل الصورة الكسول، ويحوّلها لصيغ حديثة. تأكد من أربعة أشياء:

  • استخدم مكوّن الصور المخصص بدل وسم الصورة الخام دائماً.
  • اضبط خاصية المقاسات بشكل صحيح إذا كانت الصورة متجاوبة.
  • اجعل الأولوية لصورة الظهور الأول فقط، لا لكل الصور.
  • عند استخدام نطاق خارجي، أضفه إلى قائمة النطاقات المسموح بها في الإعدادات.

الخطوط: استضفها ذاتياً مع عرض فوري

الخطوط الكبيرة تبطئ ظهور المحتوى لأن المتصفح ينتظر تحميلها. استخدم أداة الخطوط في Next.js لتضمين الخط تلقائياً مع أفضل سلوك للعرض:

layout.tsxtsx
import { IBM_Plex_Sans_Arabic } from "next/font/google";

const arabic = IBM_Plex_Sans_Arabic({
  subsets: ["arabic"],
  weight: ["400", "600", "700"],
  display: "swap"
});

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return <html className={arabic.className}>{children}</html>;
}

لا تعِد حساب ما لم يتغير

إن كان لديك حساب ثقيل داخل دالة العرض، استخدم useMemo للقيم المشتقة، وmemo للمكونات التي تستقبل خصائص مستقرة:

memo.tsxtsx
import { memo, useMemo } from "react";

const Chart = memo(function Chart({ points }: { points: Point[] }) {
  const max = useMemo(() => Math.max(...points.map(p => p.y)), [points]);
  return <svg>{/* ارسم المخطط */}</svg>;
});

export default Chart;

استرجاع البيانات: التوازي والتخزين المؤقت

طلبان متسلسلان يعنيان ضعف زمن الانتظار. اطلبهما معاً، واستخدم التخزين المؤقت لتفادي إعادة الجلب في كل زيارة:

data.tstypescript
// طلبان معاً
const [users, posts] = await Promise.all([getUsers(), getPosts()]);

// أو مع تخزين مؤقت
const users = unstable_cache(getUsers, ["users"], { revalidate: 300 })();

أزل شلال الانتظار

أخطر حالة ليست بيانات بطيئة، بل بيانات متسلسلة ينتظر بعضها بعضاً. إذا كان الطلب الثاني يحتاج نتيجة الأول، يمكنك أحياناً إظهار جزء من الصفحة فوراً باستخدام عنصر الانتظار:

page.tsxtsx
import { Suspense } from "react";

export default function Page() {
  return (
    <>
      <Header />
      <Suspense fallback={<ListSkeleton />}>
        <SlowList />
      </Suspense>
    </>
  );
}

قائمة تحقق سريعة

التحسينالأثر المتوقعالجهد
استخدام مكوّن الصور المخصصخفض وقت ظهور المحتوى الأولمنخفض
استضافة الخط ذاتياً مع عرض فوريتحسن في الظهور والاستقرارمنخفض
تحميل كسول للمكتبات الثقيلةإنقاص حزمة الجافاسكربت الأولىمتوسط
تقليل حدود مكونات المتصفحتقليل الترطيب وحجم الحزمةمتوسط
طلب البيانات بالتوازيتقليل زمن استجابة الخادممنخفض
عنصر الانتظار للأجزاء البطيئةاستجابة أسرع للمستخدممتوسط
القياس المستمرمعرفة الأولويات الفعليةمستمر