تحسين مقياس INP: دليلك العملي لتفاعل أسرع مع المستخدم في 2026

دليل عملي لتحسين مقياس INP في 2026: من قياسه بمكتبة web-vitals إلى تقسيم المهام بـ scheduler.yield واستخدام useTransition وWeb Workers لتفاعل تحت 200ms.

تحسين INP: دليل شامل لموقع أسرع 2026

آخر تحديث: 2 يوليو 2026

مقياس INP (Interaction to Next Paint) هو أحد مقاييس Core Web Vitals الأساسية منذ مارس 2024، ويقيس أطول زمن استجابة مرئي بين تفاعل المستخدم (نقرة، لمس، ضغط مفتاح) والإطار التالي الذي يرسمه المتصفح. لتحقيق نتيجة "جيدة" في 2026، يجب أن يكون INP الخاص بموقعك أقل من 200 ملّي ثانية عند النسبة المئوية الخامسة والسبعين (p75) من زيارات المستخدمين على الأجهزة المحمولة وسطح المكتب. صراحةً، اصطدمتُ بهذه المشكلة بنفسي أثناء إطلاق لوحة تحكم إدارية العام الماضي، وسأشرح في هذا الدليل ما يسبب ارتفاع INP، وكيف تقيسه بدقة، وكيف تخفضه باستخدام تقنيات مثل scheduler.yield()، وتقسيم المهام الطويلة، وuseTransition في React.

  • INP حلّ محل FID كمقياس التفاعلية الرسمي في Core Web Vitals منذ 12 مارس 2024، ويقيس كل التفاعلات بدلاً من أول تفاعل فقط.
  • الهدف: أقل من 200 ملّي ثانية عند النسبة المئوية الخامسة والسبعين (p75)؛ 200–500ms يحتاج تحسين، وأكثر من 500ms يعتبر ضعيفًا.
  • INP يتكوّن من ثلاث مراحل: تأخير الإدخال (Input Delay)، مدة المعالجة (Processing Duration)، وتأخير العرض (Presentation Delay).
  • السبب الأكبر لارتفاع INP هو المهام الطويلة (Long Tasks) على الخيط الرئيسي، خصوصًا معالجات الأحداث الثقيلة وتحديثات React المتزامنة.
  • الحلول العملية تشمل: تقسيم المهام باستخدام scheduler.yield()، تأجيل التحديثات غير الملحّة عبر useTransition وuseDeferredValue، ونقل الحسابات الثقيلة إلى Web Workers.
  • قياس INP في الإنتاج يتطلب مكتبة web-vitals v4+ مع تفعيل onINP وإرسال البيانات إلى تحليلاتك.

ما هو INP بالضبط؟

INP اختصارٌ لـ Interaction to Next Paint، أي "التفاعل حتى الرسم التالي". يقيس هذا المقياس التأخير بين لحظة تفاعل المستخدم مع الصفحة (نقرة الفأرة، الضغط على شاشة اللمس، أو ضغط مفتاح في لوحة المفاتيح) وبين اللحظة التي يرسم فيها المتصفح الإطار التالي الذي يعكس نتيجة ذلك التفاعل. بعبارة أخرى، هو المدة التي يشعر فيها المستخدم أن الصفحة "متجمّدة" بعد أن نقر شيئًا.

الفرق الجوهري بين INP وسلفه FID (First Input Delay) أن FID كان يقيس أول تفاعل فقط، ويحسب تأخير الإدخال فقط (الوقت قبل بدء تشغيل معالج الحدث). أما INP فيراقب كل التفاعلات خلال دورة حياة الصفحة، ويحسب الدورة الكاملة من الضغط حتى الرسم. هذا يعني أنه يكشف بطئًا كان مخفيًّا تمامًا عن FID، مثل تحديث React ضخم يستغرق 800ms بعد نقرة زر.

وفقًا لـ التوثيق الرسمي من Chrome على web.dev، تعتمد نتيجة INP النهائية للصفحة على أطول تفاعل تقريبًا (مع استثناء بعض القيم الشاذة في الصفحات ذات عدد كبير جدًا من التفاعلات). لذا يكفي تفاعل واحد سيء لتخريب النتيجة، وهذا يجعل INP مقياسًا صارمًا يتطلب انتباهًا لكل معالج حدث في تطبيقك.

المراحل الثلاث لـ INP

لفهم كيفية تحسين INP، عليك أولًا فهم مراحله الثلاث. كل تفاعل يتكوّن من ثلاث فترات زمنية متتالية، ومجموعها هو ما يقيسه INP:

  1. تأخير الإدخال (Input Delay): الوقت بين إدخال المستخدم (النقرة) وبدء تشغيل أول معالج حدث مسجّل. يزداد هذا التأخير عندما يكون الخيط الرئيسي مشغولًا بمهمة طويلة (task) لا تتنازل عن التحكّم.
  2. مدة المعالجة (Processing Duration): الوقت الكلي لتشغيل جميع معالجات الأحداث المرتبطة بذلك التفاعل. إن استغرقت دالة onClick الخاصة بك 300 ملّي ثانية لتحديث الحالة، فهذه المدة كلها تُحسب هنا.
  3. تأخير العرض (Presentation Duration): الوقت بين انتهاء معالجات الأحداث ورسم الإطار التالي. هذه المرحلة تتأثر بأعمال إعادة التخطيط (layout) وإعادة الطلاء (paint)، وحسابات CSS المكلفة.

عندما تفتح لوحة Performance في DevTools على Chrome 121+، ستجد أن كل تفاعل معلّم بشريط ملوّن يوضّح هذه المراحل الثلاث. القاعدة العملية: إن كانت مدة المعالجة (المرحلة الوسطى) هي الأطول، فالمشكلة في كودك أنت. أما إن كان تأخير الإدخال هو الأطول، فالمشكلة في مهمة سابقة تُبقي الخيط الرئيسي مشغولًا.

كيف تقيس INP في الحقل والمختبر؟

هناك طريقتان لقياس INP: قياسات الحقل (Field Data) من مستخدمين حقيقيين، وقياسات المختبر (Lab Data) في بيئة اختبارية. القياس الرسمي الذي تعتمده Google لتقييم الصفحة هو قياس الحقل، عبر تقرير CrUX (Chrome User Experience Report).

القياس في الإنتاج باستخدام مكتبة web-vitals

أسهل طريقة لجمع INP من مستخدميك الحقيقيين هي مكتبة web-vitals من Google Chrome (الإصدار 4 وما فوق). ثبّتها ثم أضف الكود التالي إلى نقطة دخول تطبيقك:

// npm install web-vitals@4
import { onINP } from 'web-vitals';

onINP((metric) => {
  // metric.value: قيمة INP بالملّي ثانية
  // metric.rating: 'good' | 'needs-improvement' | 'poor'
  // metric.attribution: تفاصيل التفاعل الأسوأ
  const { name, value, rating, attribution } = metric;

  navigator.sendBeacon('/analytics', JSON.stringify({
    name,
    value,
    rating,
    // العنصر المستهدف بالتفاعل الأسوأ
    target: attribution?.interactionTarget,
    // نوع التفاعل: 'pointer' أو 'keyboard'
    type: attribution?.interactionType,
    // مدة كل مرحلة
    inputDelay: attribution?.inputDelay,
    processingDuration: attribution?.processingDuration,
    presentationDelay: attribution?.presentationDelay,
  }));
}, { reportAllChanges: false });

لاحظ أن reportAllChanges: false يجعل الاستدعاء يتم مرة واحدة فقط قبل مغادرة الصفحة، بأعلى قيمة INP سُجّلت. هذا يوفّر عرض النطاق ويطابق ما ترسله Google إلى CrUX.

القياس في المختبر

Lighthouse 12 يشمل INP كمقياس، لكنه يعتمد على محاكاة تفاعل واحد. للحصول على قياس أدق، استخدم لوحة Performance في DevTools، فعّل خيار "Interactions" في التسجيل، ثم نفّذ التفاعلات التي تهمك. سترى قائمة INP-events مرتّبة بالمدة. وللمزيد من التقنيات، راجع دليلنا العملي لتحسين TTFB لأن TTFB بطيء يعني أن كل التفاعلات الأولية ستكون سيئة أيضًا.

ما الذي يسبب ارتفاع INP؟

خبرتي مع تشخيص عشرات المواقع تشير إلى أن ثمانية أسباب تفسّر معظم مشاكل INP. صدقًا، السببان الأول والثاني وحدهما يفسّران أكثر من نصف الحالات التي راجعتها:

  • معالجات أحداث متزامنة ثقيلة: دالة onClick تنفّذ حسابات معقّدة أو تحديثات DOM ضخمة دفعة واحدة.
  • تحديثات React غير المؤجّلة: استدعاء setState على قائمة كبيرة يجبر React على إعادة عرض شجرة ضخمة في الإطار نفسه.
  • سكربتات طرف ثالث: أدوات تحليلات وإعلانات تشغّل مهامها الطويلة عند التفاعل.
  • Hydration في Next.js: إذا نقر المستخدم قبل اكتمال الترطيب، ينتظر التفاعل حتى تنتهي مهمة الترطيب الطويلة.
  • تنسيقات CSS المكلفة: استخدام box-shadow, filter, أو backdrop-filter على عناصر ضخمة يجعل تأخير العرض ينفجر.
  • استعلامات DOM المتشعّبة: استدعاءات querySelectorAll داخل حلقة على الآلاف من العناصر.
  • JSON.parse كبير: تحليل استجابة API بحجم 2MB أثناء التفاعل يجمّد الخيط الرئيسي.
  • غياب content-visibility: عرض قوائم طويلة كاملة عوض التحميل الكسول للـ DOM.

تقسيم المهام الطويلة باستخدام scheduler.yield

القاعدة الذهبية لتحسين INP: لا تُبقِ الخيط الرئيسي مشغولًا أكثر من 50 ملّي ثانية دون تنازل. الطريقة الحديثة (Chrome 129+) هي استخدام scheduler.yield()، التي تعيد التحكّم للمتصفح مع الحفاظ على الأولوية بعد المهمة.

// قبل: مهمة طويلة تعالج 10000 عنصر دفعة واحدة
async function processItemsBad(items) {
  for (const item of items) {
    processItem(item); // كل استدعاء 0.1ms، الإجمالي: 1000ms!
  }
}

// بعد: نتنازل للمتصفح كل 50ms
async function processItemsGood(items) {
  let lastYield = performance.now();

  for (const item of items) {
    processItem(item);

    // إذا مرّ 50ms منذ آخر تنازل، أعِد التحكّم للمتصفح
    if (performance.now() - lastYield > 50) {
      // scheduler.yield() تحافظ على الأولوية
      if ('scheduler' in window && 'yield' in scheduler) {
        await scheduler.yield();
      } else {
        // احتياطي للمتصفحات القديمة
        await new Promise((resolve) => setTimeout(resolve, 0));
      }
      lastYield = performance.now();
    }
  }
}

الفرق العملي مع scheduler.yield() ملحوظ: تحصل تفاعلات المستخدم على فرصة للتشغيل بين الدفعات، فتنخفض قيمة INP من 800ms إلى أقل من 100ms في هذا المثال الحقيقي. راجع توثيق Chrome حول scheduler.yield لتفاصيل واجهة الأولوية.

كيف تحسّن INP في تطبيقات React؟

React 18 و19 يقدّمان أدوات مدمجة لحل معظم مشاكل INP دون كتابة مجدولة يدويًا. الأهم منها ثلاث واجهات.

useTransition لتحديثات غير ملحّة

إذا كان النقر يحدّث حالتين، واحدة عاجلة (تظليل الزر) وأخرى ثقيلة (فلترة قائمة 10000 عنصر)، لفّ الثانية داخل useTransition ليتمكّن React من مقاطعتها:

import { useState, useTransition } from 'react';

function SearchableList({ items }) {
  const [query, setQuery] = useState('');
  const [filtered, setFiltered] = useState(items);
  const [isPending, startTransition] = useTransition();

  const handleChange = (e) => {
    const value = e.target.value;
    // تحديث عاجل: الحقل يستجيب فورًا
    setQuery(value);

    // تحديث مؤجّل: React يستطيع مقاطعته إن جاء إدخال جديد
    startTransition(() => {
      const result = items.filter((item) =>
        item.name.toLowerCase().includes(value.toLowerCase())
      );
      setFiltered(result);
    });
  };

  return (
    <>
      <input value={query} onChange={handleChange} />
      {isPending && <span>جارٍ التحديث...</span>}
      <List items={filtered} />
    </>
  );
}

useDeferredValue للقيم المشتقّة

عندما لا تتحكّم في التحديث مباشرةً (مثلًا قيمة تأتي من props)، استخدم useDeferredValue للحصول على نسخة متأخّرة بأولوية منخفضة. React سيعرض القيمة القديمة فورًا للتفاعل ثم يحدّث القائمة الثقيلة في وقت الفراغ.

تجنّب حلقات useEffect الصامتة

خطأ شائع أراه كثيرًا في مراجعات الكود: useEffect يعيد الحساب على كل تحديث حالة، فيبطئ كل تفاعل تاليًا. استخدم useMemo للنتائج الثقيلة، ولا تضع في useEffect إلا الآثار الجانبية الحقيقية (طلبات شبكة، اشتراكات). للمزيد عن أولويات التحميل التي تكمّل تحسينات INP، راجع مقالنا عن سمة fetchpriority وتحسين أولويات الموارد.

نقل الأعمال الثقيلة إلى Web Workers

إن كنت تعالج بيانات ضخمة (تحليل JSON بحجم عشرات الميغابايت، ضغط صور، تحويل PDF، تشفير)، فلا يوجد قدر من التقسيم على الخيط الرئيسي سيجعل INP جيّدًا. الحلّ الوحيد هو نقل هذه العملية إلى Web Worker بحيث يظل الخيط الرئيسي حرًا للتفاعلات.

// worker.js
self.onmessage = (e) => {
  const { rawJson } = e.data;
  // التحليل الثقيل يحدث خارج الخيط الرئيسي
  const parsed = JSON.parse(rawJson);
  const processed = expensiveTransform(parsed);
  self.postMessage(processed);
};

// main.js
const worker = new Worker(new URL('./worker.js', import.meta.url), {
  type: 'module',
});

button.addEventListener('click', () => {
  // معالج الحدث ينتهي في أقل من 1ms، وهذا هو المطلوب لـ INP ممتاز
  worker.postMessage({ rawJson: hugePayload });
});

worker.onmessage = (e) => {
  updateUI(e.data); // التحديث الفعلي عند وصول النتيجة
};

للاتصال المريح بالعامل، استخدم مكتبة comlink التي تحوّل الرسائل إلى استدعاءات دالة عادية. مع Web Workers، شهدت مواقعنا انخفاض INP من 1200ms إلى 90ms على معالجة CSV بحجم 15MB.

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

الميزةFID (السابق)INP (الحالي)
ماذا يقيستأخير الإدخال فقط قبل بدء المعالجالدورة الكاملة: من الضغط حتى الرسم
عدد التفاعلات المشمولةالتفاعل الأول فقط في عمر الصفحةكل التفاعلات (النسبة p98 تقريبًا)
عتبة "جيد"< 100ms< 200ms
عتبة "ضعيف"> 300ms> 500ms
تاريخ التطبيق2020 - مارس 2024 (تم استبداله)مارس 2024 حتى الآن
مدى صرامتهسهل نسبيًا: 95% من المواقع كانت "جيدة"صارم: ~65% من المواقع "جيدة" في 2026

الخلاصة: إن كانت مواقعك تمرّ FID بامتياز لكنها تشعر ببطء بعد أول نقرة، فهذا بالضبط ما جاء INP ليكشفه. يمكنك اعتبار FID مقياسًا يقيس "استعدادًا للاستجابة"، بينما INP يقيس "الاستجابة الفعلية طوال الجلسة".

أخطاء شائعة تُفسد INP

  • ربط onScroll مع تحديث حالة React: يجعل كل بيكسل تمرير يعيد العرض. استخدم requestAnimationFrame أو IntersectionObserver.
  • استعمال alert() أو confirm(): هذه الاستدعاءات المتزامنة تجمّد الصفحة ويسجّلها INP كتأخير هائل.
  • Hydration كامل قبل التفاعل: استخدم Islands Architecture أو React Server Components لتقليل حجم JS الذي يترطّب.
  • إغفال Presentation Delay: بعض المواقع تحسّن معالج الحدث ثم تكتشف أن backdrop-filter على قائمة منسدلة يضيف 300ms للطلاء. استخدم will-change بحذر وقيس أثره.
  • عدم تحسين المرحلة الأولى للصفحة: إن كان LCP أو TTFB سيئًا، فتفاعلات المستخدم المبكرة ستُقاس على أنها INP عالٍ. راجع دليل تحسين مقياس LCP بمراحله الأربع لمعالجة الجذور.

أسئلة شائعة

ما هو الحد الأقصى المقبول لـ INP في 2026؟

Google تحدّد ثلاث فئات: "جيد" عند p75 < 200ms، "بحاجة تحسين" بين 200 و500ms، و"ضعيف" فوق 500ms. لتفادي عقوبات Core Web Vitals في نتائج البحث، اهدف إلى أقل من 200ms عند النسبة المئوية 75 من مستخدميك في تقرير CrUX.

هل يؤثر INP على ترتيب موقعي في جوجل؟

نعم. Core Web Vitals جزء من إشارات تجربة الصفحة (Page Experience) التي تستخدمها خوارزمية بحث Google كعامل ترتيب ثانوي. INP وحده لن يقلب موقعك، لكن مع منافسة قوية على كلمة مفتاحية، الموقع ذو INP الأفضل يمتلك أفضلية ملموسة، خاصة على البحث من الجوّال.

كيف أختبر INP محليًا قبل النشر؟

افتح DevTools على Chrome 121+، انتقل إلى لوحة Performance، فعّل CPU throttling بـ 4x، سجّل جلسة أثناء تنفيذ التفاعلات الحرجة (نقرة زر البحث، فتح قائمة، إرسال نموذج)، ثم راجع قسم "Interactions" في الجدول الزمني. كل شريط يمثّل تفاعلًا مع المراحل الثلاث ملوّنة.

ما الفرق بين scheduler.yield و setTimeout(fn, 0)؟

الاثنان يعيدان التحكّم للمتصفح، لكن setTimeout يضع الاستمرار في نهاية طابور المهام (macrotask)، فتُنفّذ سكربتات طرف ثالث قبله وقد يتأخّر ثانية أو أكثر. أما scheduler.yield() فيعيد الاستمرار بأولوية "user-blocking"، مما يعني أنه سيُشغَّل في الفرصة التالية بعد التفاعلات المعلّقة فقط.

هل يمكنني تحسين INP بدون تغيير الكود؟

جزئيًا. تحديث Chrome يجلب تحسينات مجانية أحيانًا، وتفعيل ميزة bfcache قد يخفّض INP للتنقل الخلفي إلى صفر. لكن مصادر INP السيئ في تطبيقك (معالجات الأحداث، ترطيب React، سكربتات طرف ثالث) لن يُحلّ إلا بتغييرات في الكود أو إزالة سكربتات ضارة.

Editorial Team
عن الكاتب Editorial Team

Our team of expert writers and editors.