Responsive Images: قلّل صورة LCP من 1.2MB لـ 180KB
لو صورة الهيرو عندك بتسحب 1.2MB لكل مستخدم، المقال ده هيساعدك تنزل النقل إلى حوالي 180KB للموبايل بدون ما تكسر جودة الصورة.
مستوى القارئ: متوسط
المشكلة باختصار
اللي بيحصل فعلاً إن فريق كتير بيصدّر صورة واحدة كبيرة، مثلاً 2400px JPEG، ويحطها في أعلى الصفحة. على شاشة موبايل عرضها 390px، المتصفح لسه ممكن يحمل نفس الملف الكبير. النتيجة: LCP أعلى، باقة بيانات أكثر، وتجربة أبطأ من غير سبب.
الافتراض هنا إن عندك صفحة تسويق أو صفحة منتج فيها صورة hero فوق الـ fold. عند 50K زيارة يوميًا، فرق 1MB في أول صورة يعني حوالي 50GB نقل زائد كل يوم. لو عندك CDN مدفوع أو مستخدمين على شبكة بطيئة، الرقم ده مش تفصيلة صغيرة.
المثال قبل التعريف
ركز في المثال ده: عندك صورة منتج اسمها hero.jpg حجمها 1.2MB. بدل ما تبعتها لكل الناس، هتطلع منها نسخ 640 و1280 و1920 بامتداد AVIF، ومعاهم WebP كـ fallback. المتصفح يختار النسخة الأنسب حسب عرض الشاشة وكثافة البكسلات.
التعريف العلمي ببساطة: srcset قائمة مرشحين للصورة، وsizes بتقول للمتصفح الصورة هتاخد عرض قد إيه في layout. عنصر picture يضيف اختيار format، مثل AVIF أولاً ثم WebP ثم JPEG. أفضل طريقة هنا إنك تدي المتصفح معلومات كفاية، بدل ما تجبره يحمل أكبر ملف.
جهّز الصور بسطرين
استخدم Sharp في build step. ده مناسب لو الصور عندك ضمن المشروع أو بتتولد قبل النشر. المكسب: ملفات أصغر واختيار ذكي في المتصفح. التكلفة: وقت build أطول ومجلد assets فيه نسخ أكثر.
npm install sharp
mkdir -p public/img
node scripts/build-hero-images.mjs
// scripts/build-hero-images.mjs
import sharp from 'sharp';
const input = 'assets/hero.jpg';
const widths = [640, 1280, 1920];
for (const width of widths) {
await sharp(input)
.resize({ width, withoutEnlargement: true })
.avif({ quality: 55 })
.toFile(`public/img/hero-${width}.avif`);
await sharp(input)
.resize({ width, withoutEnlargement: true })
.webp({ quality: 72 })
.toFile(`public/img/hero-${width}.webp`);
}
استخدم picture/srcset صح
الكود المهم في الصفحة نفسها. لا تكتب srcset من غير sizes لو الصورة responsive، لأن المتصفح وقتها هيخمن، وغالبًا هيختار ملف أكبر من اللازم.
<picture>
<source
type="image/avif"
srcset="/img/hero-640.avif 640w,
/img/hero-1280.avif 1280w,
/img/hero-1920.avif 1920w"
sizes="(max-width: 768px) 100vw, 960px" />
<source
type="image/webp"
srcset="/img/hero-640.webp 640w,
/img/hero-1280.webp 1280w,
/img/hero-1920.webp 1920w"
sizes="(max-width: 768px) 100vw, 960px" />
<img
src="/img/hero-1280.webp"
width="1280"
height="720"
alt="لوحة تعرض منتج SaaS على شاشة تحكم"
fetchpriority="high"
decoding="async" />
</picture>لاحظ إني ما استخدمتش loading="lazy" لصورة LCP فوق الصفحة. الطريقة الشائعة دي بتفشل هنا لأنها ممكن تؤخر أهم صورة في الصفحة. استخدم lazy للصور تحت الـ fold فقط.
القياس قبل وبعد
في اختبار عملي على صفحة hero واحدة، الأرقام المعقولة تكون كده: JPEG واحد 1.2MB، LCP حوالي 3.4 ثانية على Fast 3G. بعد AVIF وsrcset، الموبايل حمل 180KB، وLCP نزل لحوالي 2.1 ثانية. الأرقام دي تقديرية، لكنها واقعية لو المشكلة الأساسية كانت الصورة.
قِس بنفسك من Chrome DevTools. افتح Network، فعّل Disable cache، واختار throttling قريب من جمهورك. بعد كده راقب حجم ملف الصورة المختار وLCP من Lighthouse أو Performance panel.
الـ trade-off هنا
- بتكسب نقل أقل، LCP أفضل، وتجربة موبايل أوضح.
- بتخسر بساطة ملف واحد، لأنك هتدير 6 ملفات أو أكثر لكل صورة مهمة.
- AVIF ممتاز في الحجم، لكنه أبطأ في التوليد من WebP. خليه في build، مش وقت الطلب.
- لو الصور بتيجي من CMS، الأفضل تستخدم image CDN يدعم resizing وformat negotiation بدل سكربت محلي.
متى لا تستخدم هذه الطريقة
لا تبدأ بـ AVIF وsrcset لو الصفحة بطيئة بسبب JavaScript حجمه 3MB أو API بياخد ثانيتين. عالج السبب الأكبر الأول. كمان لا تعمل 10 أحجام لكل صورة صغيرة داخل card؛ هتزود تعقيد build من غير عائد واضح. استخدمها للصور الكبيرة والمؤثرة على LCP، خصوصًا hero وصور المنتج الأساسية.
مصادر اعتمد عليها
الخطوة التالية
افتح أكبر صورة فوق الـ fold في موقعك، واطلع منها 640 و1280 و1920 بصيغتي AVIF وWebP. بعدها قارن LCP وحجم النقل قبل وبعد في DevTools.