أداء الويب وإمكانية الوصول

تعمّق في مقاييس Core Web Vitals

5 دقيقة للقراءة

مقاييس Core Web Vitals هي مقاييس Google الموحدة لقياس تجربة المستخدم الحقيقية. كل مقابلة واجهات أمامية في شركة مهتمة بالأداء ستختبر معرفتك بهذه المقاييس وحدودها وكيفية تحسينها.

مقاييس Core Web Vitals الثلاثة

المقياس جيد يحتاج تحسين سيئ
LCP (أكبر عنصر محتوى مرئي) ≤ 2.5 ثانية > 2.5 ثانية إلى ≤ 4.0 ثانية > 4.0 ثانية
CLS (إزاحة التخطيط التراكمية) ≤ 0.1 > 0.1 إلى ≤ 0.25 > 0.25
INP (التفاعل حتى الرسم التالي) ≤ 200 مللي ثانية > 200 مللي ثانية إلى ≤ 500 مللي ثانية > 500 مللي ثانية

LCP — أكبر عنصر محتوى مرئي

يقيس LCP مدى سرعة عرض أكبر عنصر محتوى مرئي. هذا عادةً ما يكون صورة البطل أو كتلة العنوان أو فقرة نصية كبيرة.

ما العناصر التي تُحسب لـ LCP

  • عناصر <img>
  • عناصر <image> داخل <svg>
  • صور ملصق <video>
  • عناصر بخلفية background-image محمّلة عبر CSS
  • عناصر نصية على مستوى الكتلة (<h1> و <p> وغيرها)

استراتيجيات تحسين LCP

1. استخراج CSS الحرج

استخرج CSS المطلوب للمحتوى فوق الطي وضمّنه في <head>:

<head>
  <!-- تضمين CSS الحرج للعرض الفوري -->
  <style>
    .hero { display: flex; align-items: center; min-height: 60vh; }
    .hero-title { font-size: 3rem; font-weight: 700; }
  </style>
  <!-- تحميل باقي CSS بشكل غير متزامن -->
  <link rel="preload" href="/styles/main.css" as="style"
        onload="this.onload=null;this.rel='stylesheet'">
</head>

2. تحسين تحميل الخطوط

الخطوط تحجب العرض افتراضيًا. استخدم font-display: swap لعرض النص البديل فورًا:

@font-face {
  font-family: 'CustomFont';
  src: url('/fonts/custom.woff2') format('woff2');
  font-display: swap; /* عرض الخط البديل حتى يُحمّل المخصص */
}

حمّل ملف الخط مسبقًا ليبدأ التنزيل باكرًا:

<link rel="preload" href="/fonts/custom.woff2" as="font"
      type="font/woff2" crossorigin>

3. تحسين الصور

استخدم التنسيقات الحديثة والأحجام المتجاوبة والتحميل الكسول للصور خارج الشاشة:

<!-- صورة البطل: تحميل مسبق لأنها عنصر LCP -->
<link rel="preload" as="image" href="/hero.avif"
      imagesrcset="/hero-400.avif 400w, /hero-800.avif 800w, /hero-1200.avif 1200w"
      imagesizes="100vw">

<!-- صورة متجاوبة بتنسيقات حديثة -->
<picture>
  <source srcset="/hero-400.avif 400w, /hero-800.avif 800w, /hero-1200.avif 1200w"
          sizes="100vw" type="image/avif">
  <source srcset="/hero-400.webp 400w, /hero-800.webp 800w, /hero-1200.webp 1200w"
          sizes="100vw" type="image/webp">
  <img src="/hero-800.jpg" alt="وصف صورة البطل"
       width="1200" height="600"
       fetchpriority="high">
</picture>

<!-- صور أسفل الطي: تحميل كسول -->
<img src="/feature.webp" alt="لقطة شاشة الميزة"
     loading="lazy" width="600" height="400">

4. التحميل المسبق للموارد الأساسية

أخبر المتصفح بإعطاء الأولوية للموارد الحرجة لـ LCP:

<link rel="preload" href="/api/hero-data" as="fetch" crossorigin>
<link rel="preconnect" href="https://cdn.example.com">
<link rel="dns-prefetch" href="https://analytics.example.com">

CLS — إزاحة التخطيط التراكمية

يقيس CLS الاستقرار البصري. في كل مرة يتحرك فيها عنصر مرئي بشكل غير متوقع، يساهم ذلك في درجة CLS.

الأسباب الشائعة لإزاحات التخطيط

1. صور بدون أبعاد

<!-- سيئ: بدون أبعاد، يسبب إزاحة تخطيط عند تحميل الصورة -->
<img src="/photo.jpg" alt="صورة">

<!-- جيد: أبعاد صريحة تحجز المساحة -->
<img src="/photo.jpg" alt="صورة" width="800" height="600">

<!-- جيد: نسبة العرض إلى الارتفاع بـ CSS للصور المتجاوبة -->
<style>
  .responsive-img {
    width: 100%;
    aspect-ratio: 4 / 3;
    object-fit: cover;
  }
</style>
<img src="/photo.jpg" alt="صورة" class="responsive-img">

2. محتوى مُدرج ديناميكيًا

/* حجز مساحة للمحتوى الديناميكي مثل الإعلانات أو اللافتات */
.ad-slot {
  min-height: 250px; /* حجز الارتفاع المتوقع للإعلان */
  contain: layout;   /* منع تغييرات التخطيط من التأثير على العناصر المجاورة */
}

.notification-banner {
  /* استخدم transform بدلاً من تغيير الارتفاع/الهامش */
  transform: translateY(-100%);
  transition: transform 0.3s ease;
}
.notification-banner.visible {
  transform: translateY(0);
}

3. خطوط الويب تسبب FOIT/FOUT

  • FOIT (وميض النص غير المرئي): المتصفح يخفي النص حتى يُحمّل الخط
  • FOUT (وميض النص غير المنسق): المتصفح يعرض الخط البديل ثم يبدّل
/* تقليل FOUT بمطابقة مقاييس الخط البديل */
@font-face {
  font-family: 'CustomFont';
  src: url('/fonts/custom.woff2') format('woff2');
  font-display: swap;
  /* استخدم size-adjust لتقليل إزاحة التخطيط من تبديل الخط */
  size-adjust: 105%;
  ascent-override: 90%;
  descent-override: 20%;
}

4. خاصية CSS contain

/* أخبر المتصفح أن تخطيط هذا العنصر مستقل */
.card {
  contain: layout style; /* التغييرات الداخلية لن تؤثر على التخطيط الخارجي */
}

INP — التفاعل حتى الرسم التالي

حلّ INP محل FID (تأخر الإدخال الأول) كمقياس Core Web Vital في 12 مارس 2024. هذه حقيقة مهمة في المقابلات.

الفروق بين INP و FID

FID (مُهمل) INP (الحالي)
يقيس التأخر قبل تشغيل معالج الإدخال الأول الكمون الكامل من الإدخال حتى الرسم التالي
النطاق التفاعل الأول فقط جميع التفاعلات طوال دورة حياة الصفحة
يشمل تأخر الإدخال فقط تأخر الإدخال + وقت المعالجة + تأخر العرض

ماذا يقيس INP

المستخدم ينقر زرًا
  ├── تأخر الإدخال: الوقت حتى بدء المعالج (هل الخيط الرئيسي مشغول؟)
  ├── وقت المعالجة: الوقت لتنفيذ المعالج
  └── تأخر العرض: الوقت لعرض ورسم النتيجة
  = إجمالي INP لهذا التفاعل

يُبلّغ INP عن أسوأ تفاعل (تقريبًا النسبة المئوية 98) عبر جلسة الصفحة بأكملها.

استراتيجيات تحسين INP

1. تقسيم المهام الطويلة

الخيط الرئيسي لا يمكنه فعل شيء واحد فقط في المرة. المهام الطويلة (> 50 مللي ثانية) تحجب التفاعلات:

// سيئ: مهمة طويلة واحدة تحجب الخيط الرئيسي
function processLargeList(items) {
  items.forEach(item => {
    expensiveOperation(item); // تحجب لمدة 200 مللي ثانية إجمالًا
  });
}

// جيد: التنازل للخيط الرئيسي بين الدفعات
async function processLargeList(items) {
  const CHUNK_SIZE = 50;
  for (let i = 0; i < items.length; i += CHUNK_SIZE) {
    const chunk = items.slice(i, i + CHUNK_SIZE);
    chunk.forEach(item => expensiveOperation(item));

    // التنازل للسماح للمتصفح بمعالجة التفاعلات المعلقة
    await new Promise(resolve => setTimeout(resolve, 0));
  }
}

// الأفضل: استخدام scheduler.yield() عند توفره
async function processLargeList(items) {
  const CHUNK_SIZE = 50;
  for (let i = 0; i < items.length; i += CHUNK_SIZE) {
    const chunk = items.slice(i, i + CHUNK_SIZE);
    chunk.forEach(item => expensiveOperation(item));

    // scheduler.yield() يحافظ على أولوية المهمة
    if ('scheduler' in globalThis && 'yield' in scheduler) {
      await scheduler.yield();
    } else {
      await new Promise(resolve => setTimeout(resolve, 0));
    }
  }
}

2. استخدام requestAnimationFrame للتحديثات البصرية

// سيئ: إجبار إعادة حساب التخطيط وسط الإطار
button.addEventListener('click', () => {
  element.style.width = '200px';
  const height = element.offsetHeight; // يُجبر تخطيطًا متزامنًا
  element.style.height = height + 'px';
});

// جيد: تجميع التحديثات البصرية في rAF
button.addEventListener('click', () => {
  requestAnimationFrame(() => {
    element.style.width = '200px';
    element.style.height = '200px';
  });
});

3. تأجيل معالجات الإدخال

function debounce(fn, delay) {
  let timer;
  return (...args) => {
    clearTimeout(timer);
    timer = setTimeout(() => fn(...args), delay);
  };
}

// تأجيل إدخال البحث لتجنب المعالجة عند كل ضغطة مفتاح
const searchInput = document.querySelector('#search');
searchInput.addEventListener('input', debounce((e) => {
  filterResults(e.target.value);
}, 300));

قياس Core Web Vitals

تبويب الأداء في Chrome DevTools

  1. افتح DevTools → تبويب Performance
  2. فعّل خانة "Web Vitals"
  3. انقر Record، تفاعل مع الصفحة، ثم Stop
  4. يعرض الجدول الزمني LCP وإزاحات CLS وأحداث التفاعل مع مدتها

Lighthouse

يوفر Lighthouse درجة مبنية على بيانات مخبرية. شغّله من DevTools → تبويب Lighthouse، أو عبر سطر الأوامر:

npx lighthouse https://example.com --output=json --output-path=./report.json

مكتبة JavaScript web-vitals

import { onLCP, onCLS, onINP } from 'web-vitals';

onLCP(metric => console.log('LCP:', metric.value));
onCLS(metric => console.log('CLS:', metric.value));
onINP(metric => console.log('INP:', metric.value));

نصيحة للمقابلات: اعلم أن Lighthouse يقيس بيانات مخبرية (ظروف محاكاة)، بينما يقيس تقرير تجربة مستخدم Chrome (CrUX) بيانات ميدانية (مستخدمون حقيقيون). تستخدم Google البيانات الميدانية لترتيب البحث.

التالي: سنستكشف أنماط الأداء المتقدمة بما في ذلك تقسيم الكود والتخزين المؤقت واستراتيجيات التخزين المؤقت. :::

اختبار

الوحدة 5: أداء الويب وإمكانية الوصول

خذ الاختبار