كيف حققت 100/100 في PageSpeed Insights دون التضحية بالتصميم
الوصول إلى 100 في PageSpeed Insights ليس حيلة CSS، ولا نتيجة حذف الصور والخطوط حتى تصبح الصفحة فارغة. وصلت إلى هذه النتيجة بعدما تعاملت مع الأداء كجزء من بنية الموقع: HTML جاهز، صورة رئيسية محسنة، CSS حرج فقط، خطوط محلية محددة، JavaScript قليل، وميزانية تمنع التراجع في كل بناء.
في تقرير PageSpeed Insights الفعلي لموقعي، المسجل في 19 أغسطس 2026، حصلت الصفحة الرئيسية على:
| الفئة | النتيجة |
|---|---|
| Performance | 100 |
| Accessibility | 100 |
| Best Practices | 100 |
| SEO | 100 |
ماذا تعني النتيجة فعلياً؟
أرقام الاختبار المكتبي كانت:
| المقياس | النتيجة | ما يقيسه |
|---|---|---|
| First Contentful Paint | 0.3 ثانية | متى يظهر أول محتوى |
| Largest Contentful Paint | 0.3 ثانية | متى يظهر أكبر عنصر مهم |
| Total Blocking Time | 0 مللي ثانية | الوقت الذي حُجب فيه المسار الرئيسي |
| Cumulative Layout Shift | 0 | مقدار حركة التخطيط غير المتوقعة |
| Speed Index | 0.3 ثانية | سرعة اكتمال المحتوى المرئي |
لكن 100 هنا نتيجة مختبرية وليست وعداً بأن كل زائر على كل هاتف وشبكة سيحصل على 0.3 ثانية. توضح وثائق PageSpeed Insights أن بيانات المختبر تأتي من Lighthouse في بيئة محاكاة، بينما تأتي بيانات المستخدمين الفعلية من CrUX خلال فترة تجميع مدتها 28 يوماً. التقرير الحالي يعرض No Data لقسم المستخدمين الحقيقيين، لذلك لا أخلط بين الإثباتين.
1. بدأت من HTML جاهز، لا من شاشة ينتظرها JavaScript
الموقع مبني باستخدام Astro في وضع static output. تُولد الصفحات وقت البناء ثم تُرسل كـ HTML جاهز من Cloudflare Pages. لا يحتاج المتصفح إلى تحميل تطبيق JavaScript كبير قبل أن يرى العنوان أو الصورة أو زر الإجراء.
هذا مهم لأن تأخير العرض لا يأتي دائماً من حجم الشبكة. قد تصل الصورة كاملة ثم تنتظر تنفيذ bundle كبير على المسار الرئيسي. تشرح وثائق Astro أن مكونات Astro تتحول إلى HTML ولا تضيف JavaScript للمتصفح بصورة افتراضية، بينما يوضح دليل SSG في Astro أن الصفحات المولدة مسبقاً تكون جاهزة للإرسال دون انتظار بناء HTML عند كل طلب.
النصيحة: إذا كانت الصفحة تعريفية أو مقالة أو معرض أعمال، اسأل أولاً: هل تحتاج فعلاً إلى تطبيق عميل كامل؟ استخدم JavaScript للتفاعل الذي يحتاجه المستخدم، وليس لإعادة بناء المحتوى الذي كان يمكن أن يصل في HTML.
2. جعلت عنصر LCP قابلاً للاكتشاف من أول استجابة
العنصر البصري الأساسي في الصفحة هو صورة شخصية حقيقية. وضعتها داخل <img> ناتج مباشرة في HTML، ولم أحمّلها بواسطة JavaScript أو كخلفية CSS متأخرة.
استخدمت معها:
loading="eager"لأنها داخل الجزء الأول من الصفحة.fetchpriority="high"لتوضيح أهميتها للمتصفح.widthوheightلحجز المساحة قبل وصول الصورة.srcsetوsizesحتى يختار المتصفح حجماً يناسب الشاشة.- WebP بجودة متوازنة بدلاً من إرسال الملف الأصلي الكبير.
<Image
src={portrait}
width={720}
height={717}
widths={[384, 512, 640, 720]}
sizes="(max-width: 560px) calc(100vw - 2rem), 31rem"
format="webp"
quality={76}
loading="eager"
fetchpriority="high"
/>
ينصح دليل تحسين LCP بأن يكون مورد LCP قابلاً للاكتشاف في HTML الأولي، وألا يوضع له lazy loading. كما توضح وثائق Fetch Priority أن fetchpriority="high" مفيد للصورة المرشحة لأن تكون LCP، بشرط ألا نمنح الأولوية المرتفعة لكل شيء.
النصيحة: لا تضغط الصورة عشوائياً فقط. اختبر الحجم المرئي الحقيقي، التنسيق، الجودة، أبعاد المصدر، وأولوية الطلب معاً.
3. أرسلت CSS الذي تحتاجه الصفحة فعلاً
في البداية كان تضمين ملف CSS كاملاً يزيل طلباً حاجباً لكنه يجعل HTML أكبر من اللازم. ثم جربت تحميل CSS الكامل بصورة غير متزامنة، فاكتشفت أن إعادة تطبيق الأنماط لاحقاً قد ترفع Speed Index أو تسبب تغيراً بصرياً.
الحل النهائي كان استخراج القواعد المستخدمة فعلياً في الصفحة الرئيسية وقت البناء، وضغطها، ووضعها داخل HTML. بعد ذلك أزلت طلب ملف CSS الخارجي من الصفحة الرئيسية فقط. بقية الصفحات بقيت تستخدم ملفاتها المعتادة.
استخدمت Beasties لتحليل HTML النهائي واستخراج CSS المطابق. النتيجة على النسخة الحية كانت خفض HTML من نحو 153 كيلوبايت إلى نحو 77 كيلوبايت، مع صفر طلبات stylesheet خارجية في الصفحة الرئيسية.
المهم هنا ليس اسم الأداة. القاعدة هي:
- لا تحجب أول رسم بملف ضخم.
- لا تضمّن كل CSS في كل صفحة.
- لا تجعل التنسيق الكامل يصل بعد أن يرى المستخدم الصفحة.
- اختبر النتيجة بصرياً، وليس عبر رقم audit فقط.
4. أعطيت الخطوط المهمة أولوية، ولم أحمّل كل اللغات
الخط جزء من الهوية، لذلك لم أستبدله بخط نظام لمجرد كسب نقاط. استخدمت ملفات WOFF2 محلية من Fontsource، وحمّلت للصفحة العربية أوزان Cairo العربية اللازمة فقط. الصفحة الإنجليزية تحمل Space Grotesk و Syne بدلاً من تنزيل كل مجموعات المحارف في كل لغة.
أضفت preload للخطوط التي يحتاجها الجزء الأول:
<link
rel="preload"
href="/fonts/cairo-arabic-700.woff2"
as="font"
type="font/woff2"
crossorigin
/>
النصيحة: حمّل الخطوط من نفس النطاق، استخدم WOFF2، اختر الأوزان الفعلية، وراجع waterfall. preload مورد لا يحتاجه الجزء الأول قد يسرق الشبكة من صورة LCP.
5. أجّلت كلفة الأقسام التي لا يراها الزائر بعد
الأقسام أسفل البطل لا تحتاج layout و paint كاملين قبل أن يصل إليها المستخدم. استخدمت content-visibility: auto مع مساحة تقديرية تحفظ استقرار التمرير:
#main-content > section,
#main-content + footer {
content-visibility: auto;
contain-intrinsic-size: auto 56rem;
}
هذا لا يحذف المحتوى من HTML ولا يمنع محركات البحث من قراءته. هو يطلب من المتصفح تأجيل جزء من كلفة العرض حتى يقترب القسم من نافذة العرض.
النصيحة: لا تطبق هذه الخاصية بلا اختبار. القيمة التقديرية الخاطئة قد تغير طول الصفحة أثناء التمرير، لذلك راقب CLS على الهاتف وسطح المكتب.
6. منعت العناصر العائمة من منافسة المحتوى
زر واتساب مفيد، لكنه كان يستطيع تغطية زر الإجراء أو الصورة على الشاشات الصغيرة. استخدمت IntersectionObserver لإخفائه ما دام زر البطل ظاهراً، ثم أظهره بعد مغادرة البطل.
هذا القرار لا يرفع PageSpeed وحده، لكنه يوضح مبدأ مهم: الأداء ليس وقتاً فقط. الصفحة السريعة التي تغطي فيها الأدوات العائمة المحتوى ليست تجربة جيدة.
7. عالجت الإتاحة و SEO داخل المكونات نفسها
الحصول على 100 في الفئات الأربع جاء من بناء الأساس بصورة صحيحة:
- لغة واتجاه صحيحان على عنصر
<html>. - رابط تخطٍ إلى المحتوى الرئيسي.
- عناوين مرتبة وعناصر HTML دلالية.
- حالات focus واضحة والتنقل بلوحة المفاتيح.
- نصوص بديلة للصور وأبعاد ثابتة.
- canonical و hreflang متبادلان للصفحات المترجمة.
- بيانات منظمة للمقال والشخص والخدمات وفتات التنقل.
- عنوان ووصف وصورة اجتماعية حقيقية لكل صفحة.
Lighthouse يستطيع اكتشاف جزء من مشكلات الإتاحة فقط. حتى التقرير نفسه يوصي بالمراجعة اليدوية. لذلك اختبرت القائمة، زر Escape، استعادة التركيز، عرض 320 بكسل، العربية والإنجليزية، وحالة reduced motion.
8. وضعت ميزانية أداء تمنع التراجع
التحسين الذي لا يملك حارساً سيتراجع عند أول صورة كبيرة أو مكتبة جديدة. أضفت تدقيقاً إلى build يرفض الناتج إذا تجاوز الحدود، ومنها:
- CSS أكبر من 120 كيلوبايت.
- JavaScript أكبر من 100 كيلوبايت للملف.
- خط أكبر من 30 كيلوبايت.
- صورة مستخدمة أكبر من 180 كيلوبايت.
- CSS حرج للصفحة الرئيسية أكبر من 48 كيلوبايت.
- وجود stylesheet حاجب داخل الصفحة الرئيسية.
وأشغل مع ذلك فحص الأنواع، اختبارات الوحدة، بناء Astro، تدقيق HTML الناتج، واختبارات Playwright على الهاتف وسطح المكتب.
النصيحة: ضع الميزانية داخل CI أو build. تذكر الرقم في README وحده لا يمنع أي تراجع.
100 لا تعني أنني توقفت عن التحسين
هذه من أهم النقاط في التجربة. رغم أن الأداء كان 100، أظهر التقرير فرصة تقدر بـ230 مللي ثانية في render-blocking requests.
توضح وثائق تسجيل Lighthouse أن النتيجة قد تتغير بسبب الشبكة، الجهاز، مسار الطلبات، وموارد الجهاز. لهذا لا أعتمد جولة واحدة:
- أشغل ثلاث جولات أو أكثر.
- أسجل الوسيط وأسوأ جولة.
- أراجع filmstrip، لا الدرجة فقط.
- أختبر الهاتف وسطح المكتب.
- أقارن قبل التعديل وبعده في الظروف نفسها.
- أتابع بيانات المستخدمين الحقيقيين عندما تصبح متاحة.
قائمة عملية يمكنك تطبيقها اليوم
- اجعل العنوان والصورة الرئيسية موجودين في HTML الأولي.
- لا تستخدم lazy loading لصورة LCP.
- أضف أبعاداً لكل صورة لمنع CLS.
- أرسل صورة بالحجم الذي سيعرضه الجهاز فعلاً.
- احذف JavaScript الذي لا يخدم تفاعلاً حقيقياً.
- افحص CSS الحاجب وحجم CSS المضمن معاً.
- استضف الخطوط محلياً وحمّل الأوزان والمجموعات المطلوبة فقط.
- أجّل الأقسام البعيدة، لكن اختبر ثبات التخطيط.
- اختبر لوحة المفاتيح، focus، اللغة، الاتجاه، والبيانات المنظمة.
- ضع حدوداً للملفات والصفحات داخل البناء.
- اختبر ثلاث مرات وسجل الوسيط.
- لا تعتبر 100 بديلاً عن CrUX أو RUM أو ملاحظات المستخدمين.
الخلاصة
وصلت إلى 100 لأن الصفحة تبدأ بمحتوى جاهز، وتكشف المورد الأهم مبكراً، ولا تجعل JavaScript بوابة للعرض، وتحمّل الصورة والخط و CSS بقدر الحاجة. ثم حولت هذه القرارات إلى اختبارات وميزانيات حتى لا تبقى النتيجة لقطة مؤقتة.
الهدف الحقيقي ليس الدائرة الخضراء. الهدف صفحة تظهر بسرعة، لا تقفز، تعمل بلوحة المفاتيح، يفهمها محرك البحث، وتبقى سريعة بعد التعديل القادم.