إطار Next.js

تحسين الخطوط في Next.js

16 دقيقة الدرس 14 من 40

مقدمة إلى تحسين الخطوط

تؤثر خطوط الويب بشكل كبير على أداء تحميل الصفحة وتجربة المستخدم. يمكن أن يتسبب تحميل الخطوط الخارجية من خدمات مثل Google Fonts في تحولات التخطيط ويبطئ موقعك. يوفر Next.js وحدة next/font التي تحسّن الخطوط تلقائيًا من خلال استضافتها ذاتيًا، والقضاء على طلبات الشبكة الخارجية، ومنع تحولات التخطيط.

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

استخدام خطوط Google

يجعل Next.js استخدام خطوط Google سهلاً بشكل لا يصدق مع التحسين التلقائي:

الاستخدام الأساسي لخط Google

// app/layout.tsx import { Inter } from 'next/font/google'; const inter = Inter({ subsets: ['latin'], }); export default function RootLayout({ children }) { return ( <html lang="en" className={inter.className}> <body>{children}</body> </html> ); }

هذا الكود:

  • يستورد خط Inter من Google Fonts
  • ينزّله في وقت البناء
  • يستضيفه ذاتيًا مع تطبيقك
  • يطبقه على جميع الصفحات عبر التخطيط الجذري

تحديد أوزان الخط

import { Roboto } from 'next/font/google'; const roboto = Roboto({ weight: ['400', '700'], subsets: ['latin'], display: 'swap', });

خط بوزن واحد

import { Playfair_Display } from 'next/font/google'; const playfair = Playfair_Display({ weight: '400', subsets: ['latin'], });

الخطوط المتغيرة

توفر الخطوط المتغيرة أنماطًا متعددة في ملف واحد:

import { Inter } from 'next/font/google'; // الخطوط المتغيرة لا تحتاج إلى تحديد الوزن const inter = Inter({ subsets: ['latin'], variable: '--font-inter', }); export default function RootLayout({ children }) { return ( <html lang="en" className={inter.variable}> <body>{children}</body> </html> ); }
// globals.css body { font-family: var(--font-inter); } h1 { font-family: var(--font-inter); font-weight: 700; }
نصيحة: الخطوط المتغيرة أكثر كفاءة من تحميل أوزان خطوط متعددة بشكل منفصل. يمكن أن يحتوي ملف خط متغير واحد على جميع الأوزان من 100 إلى 900، مما يقلل من حجم الملف الإجمالي وطلبات HTTP.

استخدام خطوط متعددة

ادمج خطوطًا متعددة للعناوين ونص الجسم والعناصر الخاصة:

// app/layout.tsx import { Inter, Playfair_Display } from 'next/font/google'; const inter = Inter({ subsets: ['latin'], variable: '--font-inter', display: 'swap', }); const playfair = Playfair_Display({ subsets: ['latin'], variable: '--font-playfair', display: 'swap', }); export default function RootLayout({ children }) { return ( <html lang="en" className={`${inter.variable} ${playfair.variable}`}> <body>{children}</body> </html> ); }
// globals.css body { font-family: var(--font-inter), sans-serif; } h1, h2, h3, h4, h5, h6 { font-family: var(--font-playfair), serif; }

مجموعات فرعية من الخطوط

قم بتحسين التحميل من خلال تحديد مجموعات الأحرف الفرعية التي تحتاجها:

import { Noto_Sans } from 'next/font/google'; const notoSans = Noto_Sans({ weight: ['400', '700'], subsets: ['latin', 'latin-ext'], // اللاتينية + اللاتينية الممتدة });

المجموعات الفرعية المتاحة (تختلف حسب الخط)

  • latin (اللغات الأوروبية الغربية)
  • latin-ext (أحرف لاتينية ممتدة)
  • cyrillic (الروسية، الأوكرانية، إلخ)
  • greek (اللغة اليونانية)
  • arabic (اللغات العربية)
  • hebrew (اللغة العبرية)
  • vietnamese (اللغة الفيتنامية)
  • وغيرها الكثير...

مثال خط عربي

import { Cairo } from 'next/font/google'; const cairo = Cairo({ weight: ['400', '700'], subsets: ['arabic', 'latin'], variable: '--font-cairo', });
تحذير: قم فقط بتضمين المجموعات الفرعية التي تحتاجها فعليًا. تضمين مجموعات فرعية غير ضرورية يزيد من حجم ملف الخط ووقت التحميل. لموقع باللغة الإنجليزية فقط، استخدم فقط المجموعة الفرعية 'latin'.

الخطوط المحلية

استخدم خطوطًا مخصصة مخزنة في مشروعك:

خط محلي أساسي

// app/layout.tsx import localFont from 'next/font/local'; const myFont = localFont({ src: './fonts/MyCustomFont.woff2', display: 'swap', }); export default function RootLayout({ children }) { return ( <html lang="en" className={myFont.className}> <body>{children}</body> </html> ); }

ملفات خطوط متعددة (أوزان مختلفة)

import localFont from 'next/font/local'; const myFont = localFont({ src: [ { path: './fonts/MyFont-Regular.woff2', weight: '400', style: 'normal', }, { path: './fonts/MyFont-Italic.woff2', weight: '400', style: 'italic', }, { path: './fonts/MyFont-Bold.woff2', weight: '700', style: 'normal', }, ], variable: '--font-my-custom', });

خط محلي متغير

import localFont from 'next/font/local'; const myVariableFont = localFont({ src: './fonts/MyVariableFont.woff2', variable: '--font-my-variable', weight: '100 900', // نطاق الوزن للخط المتغير });

استراتيجيات عرض الخط

التحكم في كيفية عرض الخطوط أثناء التحميل:

خيارات العرض

// swap (موصى به): إظهار الاحتياطي، ثم التبديل إلى الخط المخصص const font = Inter({ display: 'swap' }); // optional: إظهار الاحتياطي لمدة 100 مللي ثانية، ثم التبديل أو الاحتفاظ بالاحتياطي const font = Inter({ display: 'optional' }); // block: إخفاء النص لمدة تصل إلى 3 ثوانٍ، ثم إظهار الخط المخصص const font = Inter({ display: 'block' }); // fallback: إظهار الاحتياطي لمدة 100 مللي ثانية، ثم التبديل مع مهلة 3 ثوانٍ const font = Inter({ display: 'fallback' }); // auto: المتصفح يقرر const font = Inter({ display: 'auto' });
موصى به: استخدم display: 'swap' في معظم الحالات. يعرض النص على الفور مع خط احتياطي، ثم ينتقل بسلاسة إلى خطك المخصص عند تحميله. هذا يمنع النص غير المرئي ويحسن الأداء المتصور.

تطبيق الخطوط على مكونات محددة

قم بتطبيق الخطوط على مكونات فردية بدلاً من عالميًا:

خط على مستوى المكون

// app/components/Heading.tsx import { Playfair_Display } from 'next/font/google'; const playfair = Playfair_Display({ weight: '700', subsets: ['latin'], }); export default function Heading({ children }) { return ( <h1 className={playfair.className}> {children} </h1> ); }

تطبيق خط شرطي

// app/page.tsx import { Inter, Roboto_Mono } from 'next/font/google'; const inter = Inter({ subsets: ['latin'] }); const robotoMono = Roboto_Mono({ subsets: ['latin'] }); export default function Page() { return ( <div> <p className={inter.className}>نص عادي</p> <code className={robotoMono.className}>مقتطف كود</code> </div> ); }

التحميل المسبق للخطوط

يقوم Next.js تلقائيًا بتحميل الخطوط المستخدمة في تطبيقك مسبقًا، ولكن يمكنك التحكم في ذلك:

import { Inter } from 'next/font/google'; const inter = Inter({ subsets: ['latin'], preload: true, // افتراضي }); // تعطيل التحميل المسبق (غير موصى به) const optionalFont = Inter({ subsets: ['latin'], preload: false, });

خطوط احتياطية

حدد خطوطًا احتياطية لاستقرار أفضل في التخطيط:

import { Inter } from 'next/font/google'; const inter = Inter({ subsets: ['latin'], fallback: ['system-ui', 'arial'], });

الخطوط الاحتياطية المعدلة

ضبط دقيق لمقاييس الاحتياطي لتطابق خطك المخصص:

import { Inter } from 'next/font/google'; const inter = Inter({ subsets: ['latin'], adjustFontFallback: true, // افتراضي، يضبط المقاييس تلقائيًا }); // أو تعطيل const noAdjust = Inter({ subsets: ['latin'], adjustFontFallback: false, });

مثال تكوين خط كامل

إعداد خط جاهز للإنتاج مع خطوط متعددة:

// app/fonts.ts import { Inter, Playfair_Display, Roboto_Mono } from 'next/font/google'; import localFont from 'next/font/local'; // خط الجسم (متغير) export const inter = Inter({ subsets: ['latin'], variable: '--font-inter', display: 'swap', }); // خط العنوان export const playfair = Playfair_Display({ weight: ['400', '700'], subsets: ['latin'], variable: '--font-playfair', display: 'swap', }); // خط الكود export const robotoMono = Roboto_Mono({ weight: ['400', '700'], subsets: ['latin'], variable: '--font-roboto-mono', display: 'swap', }); // خط العلامة التجارية المخصص export const brandFont = localFont({ src: './fonts/BrandFont-Variable.woff2', variable: '--font-brand', display: 'swap', weight: '100 900', });
// app/layout.tsx import { inter, playfair, robotoMono, brandFont } from './fonts'; export default function RootLayout({ children }) { return ( <html lang="en" className={`${inter.variable} ${playfair.variable} ${robotoMono.variable} ${brandFont.variable}`} > <body>{children}</body> </html> ); }
// app/globals.css :root { --font-body: var(--font-inter), system-ui, sans-serif; --font-heading: var(--font-playfair), Georgia, serif; --font-code: var(--font-roboto-mono), 'Courier New', monospace; --font-brand: var(--font-brand), sans-serif; } body { font-family: var(--font-body); } h1, h2, h3, h4, h5, h6 { font-family: var(--font-heading); } code, pre { font-family: var(--font-code); } .brand-text { font-family: var(--font-brand); }

أفضل ممارسات الأداء

1. استخدم الخطوط المتغيرة عندما يكون ذلك ممكنًا

// جيد: ملف خط متغير واحد const inter = Inter({ subsets: ['latin'] }); // أقل مثالية: ملفات أوزان متعددة const roboto = Roboto({ weight: ['300', '400', '500', '700', '900'], subsets: ['latin'], });

2. حدد أوزان الخط

// سيء: تحميل جميع الأوزان const tooMany = Roboto({ weight: ['100', '300', '400', '500', '700', '900'], }); // جيد: فقط الأوزان التي تستخدمها فعليًا const minimal = Roboto({ weight: ['400', '700'], });

3. استخدم المجموعات الفرعية المناسبة

// سيء: تحميل مجموعات فرعية غير ضرورية const wasteful = Noto_Sans({ subsets: ['latin', 'cyrillic', 'greek', 'arabic'], }); // جيد: المجموعة الفرعية المطلوبة فقط const efficient = Noto_Sans({ subsets: ['latin'], });

4. قلل عدد الخطوط

// الأمثل: 2-3 عائلات خطوط كحد أقصى import { Inter, Playfair_Display } from 'next/font/google'; // تجنب: عدد كبير جدًا من الخطوط المختلفة import { Inter, Roboto, Lato, OpenSans, Montserrat } from 'next/font/google';

استكشاف المشكلات الشائعة

مشكلة: الخط لا يتم تحميله

الحل: تحقق من تهجئة اسم الخط وتأكد من توفره على Google Fonts

// خطأ: اسم غير صحيح import { InterFont } from 'next/font/google'; // ❌ // صحيح import { Inter } from 'next/font/google'; // ✅

مشكلة: يحدث تحول في التخطيط

الحل: استخدم font-display: swap و adjustFontFallback

const inter = Inter({ subsets: ['latin'], display: 'swap', adjustFontFallback: true, });

مشكلة: الخطوط لا يتم تطبيقها

الحل: تأكد من تطبيق className أو variable بشكل صحيح

// تحقق من أنك تستخدم الخط <html className={inter.className}> // لـ className <html className={inter.variable}> // لمتغير CSS
تمرين: قم بإعداد نظام خط كامل لمدونة مع:
  • Inter (متغير) لنص الجسم
  • Playfair Display للعناوين
  • Fira Code لكتل الكود
  • خط محلي مخصص لشعار علامتك التجارية
قم بتكوين المجموعات الفرعية المناسبة واستراتيجيات العرض ومتغيرات CSS. أنشئ صفحة نموذجية توضح جميع الخطوط مع خطوط احتياطية مناسبة.

الملخص

يوفر تحسين الخطوط في Next.js تحسينات تلقائية للأداء. النقاط الرئيسية:

  • استخدم next/font/google للتحسين التلقائي لخطوط Google
  • الاستضافة الذاتية تلغي الطلبات الخارجية وتحسن الخصوصية
  • الخطوط المتغيرة أكثر كفاءة من ملفات الأوزان المتعددة
  • حدد فقط المجموعات الفرعية التي تحتاجها لتقليل حجم الملف
  • استخدم display: 'swap' لأفضل تجربة مستخدم
  • قم بتطبيق الخطوط عبر className أو متغيرات CSS
  • الخطوط المحلية تعمل مع localFont للطباعة المخصصة
  • حدد عائلات الخطوط إلى 2-3 للحصول على أداء مثالي
  • يقوم Next.js تلقائيًا بتحميل الخطوط مسبقًا لمنع تحولات التخطيط