كثير من النصائح الشائعة عن أداء Next.js عامة أكثر من اللازم. هذا الدليل يركّز على التحسينات التي يمكنك قياسها فعلياً، فترى الفرق في رقم حقيقي قبل وبعد التغيير.
قِس أولاً ثم حسّن
قبل أي تعديل، سجّل خط الأساس. افحص حجم الحزمة، وقس أرقام الأداء الأساسية من Lighthouse في وضع التصفح الخاص لتفادي الكاش. بدون خط أساس لا تستطيع إثبات أن تحسينك فعل شيئاً.
# حجم الحزمة
npx @next/bundle-analyzer
# قياس الأداء
npx lighthouse http://localhost:3000 --viewاجعل المكونات تعمل على الخادم ما لم تحتج متصفحاً
أكبر مكسب في تقليل JavaScript هو عدم إرساله أصلاً. المكوّن الذي يعمل على الخادم ينفّذ هناك ولا يصل إلى المتصفح إلا الناتج النهائي. أضف توجيه_use client في أقل عدد ممكن من المواضع، ولا تضعه في ملف مشترك عالٍ في الشجرة.
// مكوّن خادم، بدون توجيه_use client
export default async function Page() {
const data = await getData();
return (
<div>
<StaticInfo items={data} />
<InteractiveChart data={data} />
</div>
);
}حمّل المكتبات الثقيلة عند الحاجة
محرر أكواد كبير أو مكتبة تواريخ ثقيلة أو أي مخطط بياني ضخم يجب ألا يدخل الحزمة الأساسية إن لم يكن مطلوباً في أول رسم. استخدم التحميل الكسول مع عنصر بديل أثناء الانتظار:
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 لتضمين الخط تلقائياً مع أفضل سلوك للعرض:
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 للمكونات التي تستقبل خصائص مستقرة:
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;استرجاع البيانات: التوازي والتخزين المؤقت
طلبان متسلسلان يعنيان ضعف زمن الانتظار. اطلبهما معاً، واستخدم التخزين المؤقت لتفادي إعادة الجلب في كل زيارة:
// طلبان معاً
const [users, posts] = await Promise.all([getUsers(), getPosts()]);
// أو مع تخزين مؤقت
const users = unstable_cache(getUsers, ["users"], { revalidate: 300 })();أزل شلال الانتظار
أخطر حالة ليست بيانات بطيئة، بل بيانات متسلسلة ينتظر بعضها بعضاً. إذا كان الطلب الثاني يحتاج نتيجة الأول، يمكنك أحياناً إظهار جزء من الصفحة فوراً باستخدام عنصر الانتظار:
import { Suspense } from "react";
export default function Page() {
return (
<>
<Header />
<Suspense fallback={<ListSkeleton />}>
<SlowList />
</Suspense>
</>
);
}قائمة تحقق سريعة
| التحسين | الأثر المتوقع | الجهد |
|---|---|---|
| استخدام مكوّن الصور المخصص | خفض وقت ظهور المحتوى الأول | منخفض |
| استضافة الخط ذاتياً مع عرض فوري | تحسن في الظهور والاستقرار | منخفض |
| تحميل كسول للمكتبات الثقيلة | إنقاص حزمة الجافاسكربت الأولى | متوسط |
| تقليل حدود مكونات المتصفح | تقليل الترطيب وحجم الحزمة | متوسط |
| طلب البيانات بالتوازي | تقليل زمن استجابة الخادم | منخفض |
| عنصر الانتظار للأجزاء البطيئة | استجابة أسرع للمستخدم | متوسط |
| القياس المستمر | معرفة الأولويات الفعلية | مستمر |