المقال ده هيخليك تحط بوابة أداء آلية على كل Pull Request. لو زمن تحميل الصفحة (LCP) زاد أو حصل Layout Shift، الـ build بيفشل قبل ما الكود يوصل للمستخدمين.
أتمتة قياس Core Web Vitals بـ Lighthouse CI
المشكلة باختصار
موقعك بيبقى سريع يوم اللي بتطلقه. بعد 3 شهور و40 deploy، نفس الصفحة بقت بتحمّل في 4 ثواني بدل 1.8. مفيش حد شاف التراجع لأنه حصل بالتدريج: صورة زادت 200KB هنا، مكتبة JavaScript اتضافت هناك، خط ويب جديد، وهكذا. Google بتعاقبك في الترتيب، والمستخدم بيقفل التاب قبل ما يخلّص التحميل. الحل مش إنك تقيس يدويًا كل أسبوع — الحل إن الـ CI يرفض أي PR بيخلي الأرقام تسوء.
ما هو Lighthouse CI؟ (بمثال أولًا)
تخيّل إن عندك حارس عند باب الإنتاج. كل PR بيجي الحارس ده بيفتح موقعك بنسخة الكود الجديدة، يقيس 5 أرقام (أشهرهم LCP و CLS و TBT)، ويقارنهم بالحدود اللي انت حطيتها. لو الأرقام فوق الحد، الحارس بيقفل الباب (الـ build بيفشل) والـ PR مبيتدمجش.
بشكل علمي أدق: Lighthouse CI هو أداة رسمية من Google مبنية على Lighthouse (نفس المحرك اللي موجود في Chrome DevTools). بتشتغل headless في بيئة الـ CI، بتعمل audit كامل لكل URL بتديهوله، وبتطبّق assertions على نتائج الـ audit. اللي بيميّزها عن تشغيل Lighthouse يدويًا إنها بتعمل متوسط عدة runs (3 افتراضيًا) علشان تقلل الـ flakiness، وبتخزن النتائج التاريخية علشان تقدر تقارن.
Core Web Vitals — ما تقيسه بالظبط
قبل ما تضبط الـ budgets، لازم تفهم الأرقام بمعناها العملي:
- LCP (Largest Contentful Paint): الوقت من أول ما اليوزر فتح الصفحة لحد ما أكبر عنصر فيها (غالبًا صورة hero أو heading) ظهر. الحد الصحي من Google: أقل من 2.5 ثانية.
- CLS (Cumulative Layout Shift): مجموع الحركات المفاجئة في تخطيط الصفحة أثناء التحميل. تخيّل إنك بتقرا مقال وفجأة إعلان طلع فوقه فدفعه لتحت. ده CLS سيء. الحد: أقل من 0.1.
- INP (Interaction to Next Paint): الوقت بين ضغط اليوزر على زرار ورده الفعل البصري للصفحة. في CI بيتستبدل بـ TBT (Total Blocking Time) كـ proxy لأن INP محتاج تفاعل حقيقي. الحد: TBT أقل من 200ms.
الإعداد في 4 خطوات
- ثبّت الـ CLI:
npm install --save-dev @lhci/cli - أنشئ ملف
lighthouserc.jsonفي جذر المشروع. - ضيف workflow في
.github/workflows/lighthouse.yml. - اعمل PR تجريبي يخلّي LCP يزيد عمدًا، وشوف الـ build بيفشل.
ملف lighthouserc.json — إعداد فعلي
{
"ci": {
"collect": {
"url": [
"http://localhost:3000/",
"http://localhost:3000/blog",
"http://localhost:3000/pricing"
],
"startServerCommand": "npm run start",
"numberOfRuns": 3,
"settings": {
"preset": "desktop",
"throttlingMethod": "simulate"
}
},
"assert": {
"assertions": {
"categories:performance": ["error", {"minScore": 0.9}],
"categories:accessibility": ["error", {"minScore": 0.95}],
"largest-contentful-paint": ["error", {"maxNumericValue": 2500}],
"cumulative-layout-shift": ["error", {"maxNumericValue": 0.1}],
"total-blocking-time": ["warn", {"maxNumericValue": 200}],
"uses-long-cache-ttl": "off"
}
},
"upload": {
"target": "temporary-public-storage"
}
}
}