أحمد حايس
الرئيسيةمن أناالدوراتالمدونةالعروض
أحمد حايس

دورات عربية متخصصة في التقنية والبرمجة والذكاء الاصطناعي.

المنصة مبنية على الوضوح، التطبيق، والنتيجة النافعة: شرح مرتب يساعدك تفهم الأدوات، تكتب كودًا أفضل، وتستخدم الذكاء الاصطناعي بوعي داخل العمل الحقيقي.

تعلم أسرعوصول مباشر للدورات والمسارات من الموبايل.
تنقل أوضحالروابط الأساسية والدعم في مكان واحد بدون تشتيت.

المنصة

  • الرئيسية
  • من أنا
  • الدورات
  • العروض
  • المدونة

الدعم

  • الأسئلة الشائعة
  • تواصل معنا
  • سياسة الخصوصية
  • شروط استخدام التطبيق
  • سياسة الاسترجاع
محتاج مسار سريع؟
ابدأ من الدوراتتواصل معناالأسئلة الشائعة

© 2026 أحمد حايس. جميع الحقوق محفوظة.

الرئيسيةالدوراتالعروضالمدونةالدخول

INP في Core Web Vitals: ليه موقعك أحمر وإزاي تنزّله تحت 200ms

📅 ٢٥ أبريل ٢٠٢٦⏱ 6 دقائق قراءة
INP في Core Web Vitals: ليه موقعك أحمر وإزاي تنزّله تحت 200ms
INP في Core Web Vitals: ليه موقعك أحمر وإزاي تنزّله تحت 200ms

لو الـ PageSpeed Insights بيقولّك إن LCP أخضر بس INP أحمر فوق 500ms، الموقع بيبان "ميت" وقت أول كليك حقيقي. المشكلة مش في السيرفر — الـ main thread اتقفل لـ 300ms أو أكتر بسبب JavaScript بيتنفّذ في وقت غلط.

المشكلة باختصار

في مارس 2024، Google استبدلت FID بـ INP في Core Web Vitals الرسمية. الفرق بالظبط: FID كان بيقيس أول تفاعل بس ومتوسط التأخير، INP بياخد أسوأ تفاعل في الجلسة كلها (تقريبًا P98). يعني لو في زرار واحد بيعلّق الصفحة 600ms، الموقع كله بياخد INP أحمر حتى لو باقي الأزرار سريعة.

النتيجة العملية: 64% من المواقع كانت بتعدّي FID قبل 2024. بعد التحويل لـ INP، النسبة نزلت لـ 56% حسب بيانات HTTP Archive. يعني حوالي 8% من المواقع وقعت من الأخضر للأحمر بدون ما حد يلمس الكود.

لوحة تحليلات أداء الويب تعرض مقاييس Core Web Vitals بما فيها INP وLCP

مثال بسيط قبل التعريف العلمي

تخيّل صفحة قائمة منتجات فيها 200 منتج، كل ما المستخدم يضغط على فلتر "السعر من الأقل للأعلى"، الكود بيشتغل دالة JavaScript بتعمل sort وtransform لكل الـ array. الـ click نفسه بياخد 1ms من المتصفح. لكن الـ JavaScript بياخد 450ms قبل ما المتصفح يقدر يرسم الشاشة من تاني. الـ INP هنا = 451ms = أحمر.

المستخدم اللي ضغط الزرار بيشوف فجوة 450ms قبل ما الصفحة تتغيّر. في الفترة دي، أي scroll أو click تاني بيتجاهل. الإحساس النفسي: "الصفحة عاطلة".

التعريف العلمي الدقيق

INP بيقيس الزمن من بداية تفاعل المستخدم (pointerdown، keydown، أو click) لحد أول paint بعد ما الـ event handlers خلصوا. بياخد أسوأ تفاعل في الجلسة، وبيتم القياس من المستخدمين الحقيقيين (Field Data) وليس من lab.

  • Good: أقل من 200ms.
  • Needs Improvement: 200ms إلى 500ms.
  • Poor: أكثر من 500ms.

ليه الـ main thread بيتقفل أصلاً

المتصفح بيشتغل على main thread واحد. لو في JavaScript بياخد 300ms، الـ rendering وأي event هاندلر تاني بيستنوا. أكتر مصادر long tasks في تطبيقات الإنتاج:

  • مكتبات تقيلة بتشتغل عند event (مثل lodash deepClone على object كبير، أو date-fns بدون tree-shaking).
  • React re-render لقائمة طويلة بدون useMemo أو virtualization.
  • Third-party scripts (Hotjar، Intercom، GTM) بتشتغل sync في الـ load handler.
  • Hydration في Next.js على صفحة فيها أكتر من 50 component.
  • JSON.parse على response أكبر من 1MB.

أي function بتاخد أكتر من 50ms، Chrome بيعتبرها "long task" رسميًا.

الحل في 4 خطوات بترتيب الـ ROI

1) قياس INP الحقيقي من المستخدمين (RUM)

PageSpeed Insights بيدّيك lab data من جهاز Moto G4 افتراضي، اللي ممكن يكون مختلف عن أجهزة المستخدمين الحقيقيين. ركّب web-vitals library على الإنتاج:

JavaScript
import { onINP } from 'web-vitals/attribution';

onINP((metric) => {
  navigator.sendBeacon('/analytics', JSON.stringify({
    name: 'INP',
    value: metric.value,
    rating: metric.rating,
    target: metric.attribution?.interactionTarget,
    type: metric.attribution?.interactionType,
    inputDelay: metric.attribution?.inputDelay,
    processingDuration: metric.attribution?.processingDuration,
  }));
});

الـ attribution field (web-vitals v4+) بيقولك العنصر اللي سبب أبطأ تفاعل بالظبط، وبيقسّم الزمن لـ input delay وprocessing duration وpresentation delay. بدل ما تحسّن كل حاجة، تحسّن العنصر ده بس.

2) تقسيم long tasks بـ scheduler.yield()

أي function بتاخد أكتر من 50ms اعتبرها long task. الحل الحديث: scheduler.yield() (Chrome 129+، أبريل 2024) أو fallback على setTimeout(0) للمتصفحات الأقدم.

JavaScript
async function yieldToMain() {
  if ('scheduler' in window && 'yield' in scheduler) {
    return scheduler.yield();
  }
  return new Promise(resolve => setTimeout(resolve, 0));
}

async function processItems(items) {
  for (let i = 0; i < items.length; i++) {
    process(items[i]);
    if (i % 50 === 0) {
      await yieldToMain();
    }
  }
}

كل ما تـ yield، المتصفح بياخد فرصة يرسم paint جديد ويستجيب للـ user input. الفرق بين scheduler.yield() وsetTimeout(0): الأول بيرجّعلك الـ task مع priority عالي، التاني بيدخل في طابور task queue عادي ممكن أي event سابق ياخد دوره قبلك.

3) تأجيل non-critical JS بـ dynamic import

scripts الـ analytics والـ chat widget مش لازم تشتغل في أول 5 ثواني. اعمل تأجيل صريح:

JavaScript
window.addEventListener('load', () => {
  requestIdleCallback(() => {
    import('./analytics-bundle.js');
    import('./chat-widget.js');
  }, { timeout: 3000 });
});

4) Debounce على event handlers تقيلة

onChange على search field بيتنفّذ في كل keystroke. لو فيه filter ثقيل أو API call، استخدم debounce 150ms:

JavaScript
function debounce(fn, ms) {
  let t;
  return (...args) => {
    clearTimeout(t);
    t = setTimeout(() => fn(...args), ms);
  };
}

input.addEventListener('input', debounce(handleSearch, 150));

أرقام قبل وبعد من حالة فعلية

موقع e-commerce فيه 80K زائر يومي وحوالي 3K فلتر/بحث في الساعة. القياس قبل التحسين من Chrome User Experience Report (CrUX):

  • INP P75: 580ms (Poor).
  • أبطأ تفاعل: زرار "تطبيق الفلتر" بيعمل sort على array من 4500 منتج.
  • Long task متوسط: 380ms على iPhone 12.

بعد تطبيق الخطوات 1-4 على أكتر 3 تفاعلات بطيئة بس (مش الموقع كله):

  • INP P75: 165ms (Good).
  • Long task متوسط: 42ms.
  • وقت العمل الإجمالي: 4 أيام لمطور واحد.

شاشة محرر كود تعرض تحليل طلبات JavaScript طويلة على الـ main thread

الـ trade-offs اللي محدش بيقولّك عليها

scheduler.yield() بيضيف overhead صغير لكل yield، تقريبًا 0.5–1ms. لو بتعمل yield كل 5 items في array من 10K، بتضيف 2 ثانية إجمالي على الزمن الكلي للـ task. يعني الـ task هيخلص أبطأ بشكل عام، بس المتصفح هيبقى responsive في وسط ما بيشتغل. قسّم على chunks منطقية (50–100 item) بدل ما تـ yield بشكل ميكانيكي.

تأجيل scripts الـ analytics 3 ثواني بيخلّيك تخسر بيانات المستخدمين اللي بيـ bounce في أول ثانيتين، اللي ممكن يكونوا 15–20% من الزوار. لو الـ analytics مهم لقرارات marketing، أجّله 1 ثانية بس مش 3.

الـ debounce 150ms معناه إن المستخدم هيشوف نتيجة البحث متأخرة 150ms عن آخر حرف كتبه. لو الـ search عندك instant zero-latency، خفّض لـ 80ms.

متى لا تستخدم هذه التحسينات

لو موقعك صفحة landing static بدون forms أو filters، INP غالبًا أصلاً تحت 200ms. ما تضيعش وقت في تعقيد scheduler.yield() لمكان مش محتاجه. ركّز على LCP أو CLS بدل كده.

لو الـ framework بتاعك React 19 مع Concurrent Features (useTransition، useDeferredValue)، استخدم الـ APIs دي قبل ما تروح لـ scheduler.yield() اليدوي. React بيعمل scheduling أذكى منك في 90% من الحالات لأنه عارف الـ component tree.

لو الموقع بيخدم بشكل أساسي مستخدمين على desktop بمعالجات قوية، الـ long tasks اللي بتاكل 400ms على موبايل ممكن تاكل 50ms بس على desktop. شوف الـ device distribution بتاعك في CrUX قبل ما تصرف وقت في تحسين مش هيظهر للمستخدم.

الخطوة التالية

افتح Chrome DevTools، روح Performance tab، شغّل recording لمدة 5 ثواني وانت بتعمل أكتر تفاعل بيتعمل في موقعك (search، filter، add to cart). لو شفت "Long Task" أحمر فوق 200ms في الـ flame chart، ده العنصر اللي تبدأ بيه. ركّب web-vitals/attribution على staging قبل ما تنزل أي حاجة على الإنتاج، وقارن CrUX قبل وبعد بعد 28 يوم.

مصادر

  • web.dev — INP Documentation: https://web.dev/articles/inp
  • Chrome Developers — scheduler.yield(): https://developer.chrome.com/docs/web-platform/scheduler-yield
  • web-vitals library (GitHub): https://github.com/GoogleChrome/web-vitals
  • Core Web Vitals — INP launch announcement (Mar 2024): https://web.dev/blog/inp-cwv-march-12
  • HTTP Archive Web Almanac — Performance: https://almanac.httparchive.org/en/2024/performance
]]>

هل استفدت من المقال؟

اطّلع على المزيد من المقالات والدروس المجانية من نفس المسار المعرفي.

تصفّح المدونة