البحث عن التفاعلات البطيئة في الحقل

تعرَّف على كيفية العثور على التفاعلات البطيئة في بيانات الحقول لموقعك الإلكتروني حتى تتمكّن من العثور على فرص لتحسين مقياس "مدة عرض الاستجابة لتفاعل المستخدم".

البيانات الميدانية هي بيانات تُطلعك على تجربة المستخدمين الفعليين لموقعك الإلكتروني. ويساعدك ذلك في رصد المشاكل التي لا يمكنك العثور عليها في بيانات المختبر وحدها. في ما يتعلق بمقياس مدى استجابة الصفحة لتفاعلات المستخدم (INP)، تكون بيانات الحقل ضرورية لتحديد التفاعلات البطيئة، كما تقدّم لك أدلة مهمة لمساعدتك في حلّ المشاكل المتعلّقة بها.

في هذا الدليل، ستتعرّف على كيفية تقييم INP لموقعك الإلكتروني بسرعة باستخدام بيانات الاستخدام الفعلي من تقرير تجربة مستخدمي Chrome (CrUX) لمعرفة ما إذا كان موقعك الإلكتروني يواجه مشاكل في INP. بعد ذلك، ستتعرّف على كيفية استخدام إصدار تحديد المصدر من مكتبة JavaScript الخاصة بسمات الأداء الحيوي للويب، والإحصاءات الجديدة التي تقدّمها من Long Animation Frames API (LoAF)، وذلك لجمع بيانات الحقل وتفسيرها للتفاعلات البطيئة على موقعك الإلكتروني.

البدء باستخدام CrUX لتقييم INP لموقعك الإلكتروني

إذا لم تكن تجمع بيانات ميدانية من مستخدمي موقعك الإلكتروني، قد تكون CrUX نقطة بداية جيدة. يجمع CrUX بيانات الاستخدام الفعلي من مستخدمي Chrome الفعليين الذين وافقوا على إرسال بيانات القياس عن بُعد.

يتم عرض بيانات CrUX في عدد من المجالات المختلفة، ويعتمد ذلك على نطاق المعلومات التي تبحث عنها. يمكن أن يوفّر CrUX بيانات عن مقياس "مدى استجابة الصفحة لتفاعلات المستخدم" (INP) وغيرها من "مؤشرات أداء الويب الأساسية" لما يلي:

  • الصفحات الفردية ومصادر البيانات بالكامل باستخدام إحصاءات PageSpeed
  • أنواع الصفحات على سبيل المثال، تتضمّن العديد من المواقع الإلكترونية للتجارة الإلكترونية نوعَي "صفحة تفاصيل المنتج" و"صفحة بيانات المنتج". يمكنك الحصول على بيانات CrUX لأنواع الصفحات الفريدة في Search Console.

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

البيانات الميدانية كما تظهر في CrUX ضمن "إحصاءات أداء الصفحة"، والتي تعرض مقياسَي "سرعة عرض أكبر محتوى مرئي" (LCP) و"مدى استجابة الصفحة لتفاعلات المستخدم" (INP) و"متغيّرات التصميم التراكمية" (CLS) ضمن "مؤشرات أداء الويب الأساسية" الثلاثة، ومقياسَي "وقت استجابة خادم الويب" (TTFB) و"وقت تحميل الصفحة" (FCP) كمقاييس تشخيصية، ومقياس "مهلة الاستجابة الأولى" (FID) كمقياس متوقّف نهائيًا من "مؤشرات أداء الويب الأساسية"
قراءة لبيانات CrUX كما تظهر في "إحصاءات PageSpeed" في هذا المثال، يحتاج مقياس INP لصفحة الويب المحدّدة إلى تحسين.

هذه البيانات مفيدة لأنّها تُعلمك إذا كانت لديك مشكلة. لا يمكن لخدمة CrUX، مع ذلك، إخبارك بما يتسبّب في حدوث المشاكل. تتوفّر العديد من حلول مراقبة المستخدِمين الفعليين (RUM) التي ستساعدك في جمع بيانات الحقول الخاصة بك من مستخدِمي موقعك الإلكتروني لمساعدتك في الإجابة عن هذا السؤال، ومن الخيارات المتاحة جمع بيانات الحقول بنفسك باستخدام مكتبة JavaScript لبيانات قياس الأداء الرئيسية على الويب.

جمع بيانات تجارب المستخدمِين الحقيقيين باستخدام مكتبة JavaScript web-vitals

web-vitals مكتبة JavaScript هي نص برمجي يمكنك تحميله على موقعك الإلكتروني لجمع بيانات الحقول من مستخدمي موقعك الإلكتروني. ويمكنك استخدامها لتسجيل عدد من المقاييس، بما في ذلك INP في المتصفحات التي تتيح ذلك.

توافق المتصفّح

  • Chrome: 96
  • الحافة: 96
  • Firefox: غير متوافق
  • Safari: غير متوافق

المصدر

يمكن استخدام الإصدار العادي من مكتبة web-vitals للحصول على بيانات INP الأساسية من المستخدمين في المجال:

import {onINP} from 'web-vitals';

onINP(({name, value, rating}) => {
  console.log(name);    // 'INP'
  console.log(value);   // 512
  console.log(rating);  // 'poor'
});

لتحليل بيانات الحقول من المستخدمين، عليك إرسال هذه البيانات إلى مكان ما:

import {onINP} from 'web-vitals';

onINP(({name, value, rating}) => {
  // Prepare JSON to be sent for collection. Note that
  // you can add anything else you'd want to collect here:
  const body = JSON.stringify({name, value, rating});

  // Use `sendBeacon` to send data to an analytics endpoint.
  // For Google Analytics, see https://github.com/GoogleChrome/web-vitals#send-the-results-to-google-analytics.
  navigator.sendBeacon('/analytics', body);
});

ومع ذلك، لا تقدّم لك هذه البيانات بحد ذاتها معلومات أكثر من تلك التي تقدّمها لك CrUX. وهنا يأتي دور عملية تحديد المصدر في مكتبة "مؤشرات الأداء الرئيسية للويب".

تحقيق المزيد من النتائج باستخدام عملية إنشاء الإحالة في مكتبة web-vitals

تعرِض وحدة تحديد المصدر في مكتبة مؤشرات أداء الويب بيانات إضافية يمكنك الحصول عليها من المستخدمين في المجال لمساعدتك في تحديد المشاكل المتعلّقة بالتفاعلات التي تؤثّر في مؤشر INP لموقعك الإلكتروني وحلّها بشكل أفضل. يمكن الوصول إلى هذه البيانات من خلال عنصر attribution الذي يظهر في طريقة onINP() في المكتبة:

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

onINP(({name, value, rating, attribution}) => {
  console.log(name);         // 'INP'
  console.log(value);        // 56
  console.log(rating);       // 'good'
  console.log(attribution);  // Attribution data object
});
كيفية ظهور سجلّات وحدة التحكّم من مكتبة web-vitals تعرض وحدة التحكّم في هذا المثال اسم المقياس (INP) وقيمة INP (56) ومكان توفّر هذه القيمة ضمن حدود INP (جيد) ومختلف أجزاء المعلومات المعروضة في عنصر تحديد المصدر، بما في ذلك الإدخالات من Long Animation Frames API.
كيفية ظهور البيانات من مكتبة مؤشرات أداء الويب في وحدة التحكّم

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

  • "هل تفاعل المستخدم مع الصفحة أثناء تحميلها؟"
  • "هل تم تنفيذ معالجات أحداث التفاعل لفترة طويلة؟"
  • "هل تأخّر بدء تشغيل رمز معالِج حدث التفاعل؟ إذا كان الأمر كذلك، ما الذي كان يحدث أيضًا في سلسلة المهام الرئيسية في ذلك الوقت؟"
  • "هل أدّى التفاعل إلى زيادة كبيرة في عمليات المعالجة التي أدّت إلى تأخير عرض الإطار التالي؟"

يعرض الجدول التالي بعض بيانات تحديد المصدر الأساسية التي يمكنك الحصول عليها من المكتبة والتي يمكن أن تساعدك في تحديد بعض الأسباب الأساسية لبطء التفاعلات على موقعك الإلكتروني:

مفتاح attribution للعنصر البيانات
interactionTarget محدد CSS يشير إلى العنصر الذي أنشأ قيمة INP للصفحة، على سبيل المثال، button#save.
interactionType نوع التفاعل، سواء من النقرات أو النقرات أو إدخالات لوحة المفاتيح
inputDelay* مهلة الاستجابة للإدخال للتفاعل
processingDuration* الوقت المستغرَق منذ بدء تشغيل مستمع الأحداث الأول استجابةً لتفاعل المستخدم إلى أن تنتهي معالجة جميع مستمعي الأحداث
presentationDelay* وقت عرض التفاعل، الذي يبدأ من وقت انتهاء معالِجات الأحداث إلى وقت رسم اللقطة التالية
longAnimationFrameEntries* الإدخالات من ملفّ "الموافقة التلقائية" المرتبطة بالتفاعل يُرجى الاطّلاع على القسم التالي للحصول على معلومات إضافية.
*ميزات جديدة في الإصدار 4

بدءًا من الإصدار 4 من مكتبة web-vitals، يمكنك الحصول على إحصاءات أكثر تفصيلاً عن التفاعلات التي تتضمن مشاكل من خلال البيانات التي تقدّمها مع تقسيمات مراحل INP (وقت استجابة الإدخال ومدة المعالجة ووقت استجابة العرض) وLong Animation Frames API (LoAF).

Long Animation Frames API (LoAF)

توافق المتصفّح

  • Chrome: 123.
  • Edge: ‏ 123.
  • Firefox: غير متوافق
  • Safari: غير متوافق

المصدر

إنّ تصحيح أخطاء التفاعلات باستخدام بيانات الحقول هو مهمة صعبة. ومع ذلك، باستخدام البيانات الواردة من LoAF، أصبح من الممكن الآن الحصول على إحصاءات أفضل عن أسباب بطء التفاعلات، لأنّ LoAF يعرض عددًا من التوقيتات التفصيلية وغيرها من البيانات التي يمكنك استخدامها لتحديد الأسباب الدقيقة، والأهم من ذلك، تحديد مصدر المشكلة في رمز موقعك الإلكتروني.

تعرض نسخة تحديد المصدر من مكتبة web-vitals صفيفًا من إدخالات LoAF ضمن مفتاح longAnimationFrameEntries لعنصر attribution. يسرد الجدول التالي بعض البيانات الرئيسية التي يمكنك العثور عليها في كل إدخال في ملف "البيانات الوصفية للإعلانات على شبكة البحث":

مفتاح عنصر إدخال LoAF البيانات
duration مدة إطار الصورة المتحركة التي تستغرق وقتًا طويلاً، حتى انتهاء التنسيق، باستثناء الرسم والتركيب.
blockingDuration إجمالي الوقت الذي تعذّر فيه على المتصفّح الاستجابة بسرعة في الإطار بسبب المهام الطويلة. يمكن أن يشمل وقت الحظر هذا المهام الطويلة التي تعمل باستخدام JavaScript، بالإضافة إلى أي مهمة لاحقة طويلة لعرض الإطار.
firstUIEventTimestamp الطابع الزمني لوقت إضافة الحدث إلى "قائمة الانتظار" أثناء عرض اللقطة وهي مفيدة لمعرفة بداية تأخُّر الإدخال في التفاعل.
startTime الطابع الزمني لبدء عرض الإطار.
renderStart وقت بدء عرض اللقطة ويشمل ذلك أي استدعاءات requestAnimationFrame (واستدعاءات ResizeObserver إن أمكن)، ولكن قبل بدء أي عمل على التصميم أو التنسيق.
styleAndLayoutStart عند تطبيق نمط أو تنسيق في الإطار يمكن أن يكون مفيدًا في معرفة طول عمل التصميم/التنسيق عند احتساب الطوابع الزمنية الأخرى المتاحة.
scripts صفيف من العناصر التي تحتوي على معلومات تحديد مصدر النصوص البرمجية التي تساهم في INP للصفحة
عرض مرئي لإطار صورة متحركة يستغرق عرضها وقتًا طويلاً وفقًا لنموذج LoAF
مخطّط بياني لمواقيت عرض صور متحركة تستغرق وقتًا طويلاً وفقًا لواجهة برمجة التطبيقات LoAF API (باستثناء blockingDuration).

يمكن أن تقدّم لك كل هذه المعلومات الكثير من المعلومات حول ما يجعل التفاعل بطيئًا، ولكن يجب أن تكون صفيف scripts الذي تعرضه إدخالات LoAF ذات أهمية خاصة:

مفتاح عنصر تحديد مصدر النص البرمجي البيانات
invoker المُستخدِم الذي يُطلِب تنفيذ الإجراء ويمكن أن يختلف ذلك استنادًا إلى نوع المُستدعي الموضّح في الصف التالي. يمكن أن تكون أمثلة أدوات الاستدعاء قيمًا مثل 'IMG#id.onload' أو 'Window.requestAnimationFrame' أو 'Response.json.then'.
invokerType نوع المُستدعي يمكن أن يكون 'user-callback' أو 'event-listener' أو 'resolve-promise' أو 'reject-promise' أو 'classic-script' أو 'module-script'.
sourceURL عنوان URL للنص البرمجي الذي نشأ منه إطار الصورة المتحركة الذي يستغرق وقتًا طويلاً
sourceCharPosition موضع الحرف في النص البرمجي الذي يُحدَّد بواسطة sourceURL.
sourceFunctionName اسم الدالة في النص البرمجي المحدَّد

يحتوي كل إدخال في هذا الصفيف على البيانات المعروضة في هذا الجدول، ما يمنحك معلومات عن النص البرمجي المسؤول عن بطء التفاعل وكيفية تسببه في ذلك.

قياس الأسباب الشائعة للتفاعلات البطيئة وتحديدها

لكي نعطيك فكرة عن كيفية استخدام هذه المعلومات، سيوضّح لك هذا الدليل الآن كيفية استخدام بيانات LoAF التي تظهر في مكتبة web-vitals لتحديد بعض الأسباب الكامنة وراء بطء التفاعلات.

مدد المعالجة الطويلة

مدة معالجة التفاعل هي الوقت الذي يستغرقه تنفيذ عمليات الاستدعاء المسجّلة لمعالج الحدث للتفاعل إلى نهايته وأيّ شيء آخر قد يحدث بينهما. تعرض مكتبة web-vitals مدد المعالجة العالية:

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

onINP(({name, value, attribution}) => {
  const {processingDuration} = attribution; // 512.5
});

من الطبيعي الاعتقاد أنّ السبب الأساسي وراء بطء التفاعل هو أنّه استغرق وقتًا طويلاً لتشغيل رمز معالِج الحدث، ولكن هذا ليس هو الحال دائمًا. بعد التأكّد من أنّ هذه هي المشكلة، يمكنك التعمّق في بيانات LoAF:

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

onINP(({name, value, attribution}) => {
  const {processingDuration} = attribution; // 512.5

  // Get the longest script from LoAF covering `processingDuration`:
  const loaf = attribution.longAnimationFrameEntries.at(-1);
  const script = loaf?.scripts.sort((a, b) => b.duration - a.duration)[0];

  if (script) {
    // Get attribution for the long-running event handler:
    const {invokerType} = script;        // 'event-listener'
    const {invoker} = script;            // 'BUTTON#update.onclick'
    const {sourceURL} = script;          // 'https://example.com/app.js'
    const {sourceCharPosition} = script; // 83
    const {sourceFunctionName} = script; // 'update'
  }
});

كما هو موضّح في مقتطف الرمز البرمجي السابق، يمكنك استخدام بيانات LoAF لتتبُّع السبب الدقيق وراء التفاعل مع قيم مدة المعالجة العالية، بما في ذلك:

  • العنصر وأداة معالجة الأحداث المسجّلة له
  • ملف البرنامج النصي وموضع الحرف فيه الذي يحتوي على رمز معالِج الحدث الذي يستغرق وقتًا طويلاً
  • اسم الدالة

هذا النوع من البيانات قيّم للغاية. لم تعُد بحاجة إلى بذل الجهد اللازم لمعرفة التفاعل بالضبط أو معالِج الأحداث الذي كان مسؤولاً عن القيم العالية لطول مدة المعالجة. وبما أنّ النصوص البرمجية التابعة لجهات خارجية يمكنها غالبًا تسجيل معالجات الأحداث الخاصة بها، يمكنك تحديد ما إذا كان الرمز البرمجي الخاص بك هو المسؤول عن حدوث المشكلة أم لا. بالنسبة إلى الرمز الذي يمكنك التحكّم فيه، عليك الاطّلاع على تحسين المهام الطويلة.

تأخيرات طويلة في الإدخال

على الرغم من أنّ معالجات الأحداث التي تستغرق وقتًا طويلاً شائعة، هناك أجزاء أخرى من التفاعل يجب أخذها في الاعتبار. يحدث جزء واحد قبل مدة المعالجة، ويُعرف باسم تأخُّر الإدخال. هذا هو الوقت المستغرَق منذ بدء المستخدم التفاعل إلى بدء تنفيذ عمليات الاستدعاء لمعالج الحدث، ويحدث ذلك عندما تكون السلسلة الرئيسية تعالج مهمة أخرى. يمكن أن تُعلمك عملية تحديد المصدر في مكتبة مؤشرات الأداء الحيوية على الويب بمدة تأخُّر الإدخال لأحد التفاعلات:

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

onINP(({name, value, attribution}) => {
  const {inputDelay} = attribution; // 125.59439536
});

إذا لاحظت أنّ بعض التفاعلات تتضمن تأخيرات عالية في الإدخال، عليك معرفة ما كان يحدث على الصفحة في وقت التفاعل الذي أدّى إلى تأخّر الإدخال، ويعود ذلك غالبًا إلى ما إذا كان التفاعل قد حدث أثناء تحميل الصفحة أو بعدها.

هل حدث ذلك أثناء تحميل الصفحة؟

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

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

onINP(({name, value, attribution}) => {
  const {inputDelay} = attribution; // 125.59439536

  // Get the longest script from the first LoAF entry:
  const loaf = attribution.longAnimationFrameEntries[0];
  const script = loaf?.scripts.sort((a, b) => b.duration - a.duration)[0];

  if (script) {
    // Invoker types can describe if script eval blocked the main thread:
    const {invokerType} = script;    // 'classic-script' | 'module-script'
    const {sourceLocation} = script; // 'https://example.com/app.js'
  }
});

إذا سجّلت هذه البيانات في الحقل ولاحظت تأخيرات إدخال عالية وأنواع 'classic-script' أو 'module-script' للمشغِّل، من المنطقي القول إنّ النصوص البرمجية على موقعك الإلكتروني تستغرق وقتًا طويلاً لتقييمها، وتحظر السلسلة الرئيسية لفترة طويلة بما يكفي لتأخير التفاعلات. يمكنك تقليل وقت الحظر هذا من خلال تقسيم النصوص البرمجية إلى حِزم أصغر، وتأجيل تحميل الرمز غير المستخدَم في البداية إلى وقت لاحق، وتدقيق موقعك الإلكتروني بحثًا عن الرمز غير المستخدَم الذي يمكنك إزالته بالكامل.

هل حدث ذلك بعد تحميل الصفحة؟

على الرغم من أنّ تأخّر الإدخال يحدث غالبًا أثناء تحميل الصفحة، إلا أنّه من الممكن أن يحدث بعد تحميل الصفحة بسبب سبب مختلف تمامًا. يمكن أن تكون الأسباب الشائعة لتأخُّر الإدخال بعد تحميل الصفحة هي الرمز البرمجي الذي يتم تشغيله بشكل دوري بسبب طلب setInterval سابق، أو حتى عمليات استدعاء الأحداث التي تم وضعها في قائمة الانتظار لتشغيلها في وقت سابق، ولا تزال قيد المعالجة.

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

onINP(({name, value, attribution}) => {
  const {inputDelay} = attribution; // 125.59439536

  // Get the longest script from the first LoAF entry:
  const loaf = attribution.longAnimationFrameEntries[0];
  const script = loaf?.scripts.sort((a, b) => b.duration - a.duration)[0];

  if (script) {
    const {invokerType} = script;        // 'user-callback'
    const {sourceURL} = script;          // 'https://example.com/app.js'
    const {sourceCharPosition} = script; // 83
    const {sourceFunctionName} = script; // 'update'
  }
});

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

  • يشير الرمز 'user-callback' إلى أنّ مهمة الحظر كانت من setInterval أو setTimeout أو حتى requestAnimationFrame.
  • يشير الرمز 'event-listener' إلى أنّ مهمة الحظر مصدرها إدخال سابق تم وضعه في قائمة الانتظار ولا تزال قيد المعالجة.
  • تشير الحالة 'resolve-promise' والحالة 'reject-promise' إلى أنّ مهمة الحظر كانت من بعض الأعمال غير المتزامنة التي تم بدءها في وقت سابق، وتم حلّها أو رفضها في وقت حاول فيه المستخدم التفاعل مع الصفحة، ما أدّى إلى تأخير التفاعل.

في جميع الأحوال، ستمنحك بيانات تحديد مصدر النصوص البرمجية فكرة عن المكان الذي يجب أن تبدأ فيه البحث، وما إذا كان تأخُّر الإدخال ناتجًا عن الرمز البرمجي الخاص بك أو رمز برمجي تابع لجهة خارجية.

تأخيرات طويلة في عرض المحتوى

تُعدّ تأخيرات العرض المرحلة الأخيرة من التفاعل، وتبدأ عند انتهاء معالجات أحداث التفاعل، وإلى أن يتم رسم اللقطة التالية. تحدث هذه التغييرات عندما يؤدي العمل في معالِج حدث إلى تغيير الحالة المرئية لواجهة المستخدم بسبب تفاعل. كما هو الحال مع مدد المعالجة وتأخُّر الإدخال، يمكن أن تُعلمك مكتبة مؤشرات الأداء الرئيسية على الويب بمدة تأخُّر عرض تفاعل معيّن:

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

onINP(({name, value, attribution}) => {
  const {presentationDelay} = attribution; // 113.32307691
});

إذا سجّلت هذه البيانات ولاحظت تأخيرات كبيرة في عرض التفاعلات التي تساهم في INP لموقعك الإلكتروني، يمكن أن تختلف الأسباب، ولكن إليك سببان يجب الانتباه إليهما.

أعمال التصميم والتنسيق المُكلفة

قد تكون التأخيرات الطويلة في عرض المحتوى ناتجة عن عمليات إعادة احتساب الأنماط والتنسيق المُكلفة التي تنتج عن عدد من الأسباب، بما في ذلك أدوات اختيار CSS المعقدة وأحجام DOM الكبيرة. يمكنك قياس مدة هذا العمل باستخدام أوقات LoAF المعروضة في مكتبة مؤشرات الأداء الرئيسية للويب:

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

onINP(({name, value, attribution}) => {
  const {presentationDelay} = attribution; // 113.32307691

  // Get the longest script from the last LoAF entry:
  const loaf = attribution.longAnimationFrameEntries.at(-1);
  const script = loaf?.scripts.sort((a, b) => b.duration - a.duration)[0];

  // Get necessary timings:
  const {startTime} = loaf; // 2120.5
  const {duration} = loaf;  // 1002

  // Figure out the ending timestamp of the frame (approximate):
  const endTime = startTime + duration; // 3122.5

  // Get the start timestamp of the frame's style/layout work:
  const {styleAndLayoutStart} = loaf; // 3011.17692309

  // Calculate the total style/layout duration:
  const styleLayoutDuration = endTime - styleAndLayoutStart; // 111.32307691

  if (script) {
    // Get attribution for the event handler that triggered
    // the long-running style and layout operation:
    const {invokerType} = script;        // 'event-listener'
    const {invoker} = script;            // 'BUTTON#update.onclick'
    const {sourceURL} = script;          // 'https://example.com/app.js'
    const {sourceCharPosition} = script; // 83
    const {sourceFunctionName} = script; // 'update'
  }
});

لن يخبرك LoAF بمدة عمل التصميم والتنسيق لإطار معيّن، ولكنه سيخبرك بموعد بدء العمل. باستخدام الطابع الزمني لبدء العمل هذا، يمكنك استخدام بيانات أخرى من LoAF لاحتساب مدة دقيقة لهذا العمل من خلال تحديد وقت انتهاء الإطار، وطرح الطابع الزمني لبدء العمل على التصميم والتنسيق منه.

عمليات استدعاء requestAnimationFrame التي تستغرق وقتًا طويلاً

من الأسباب المحتمَلة للتأخيرات الطويلة في العرض هي العمل المفرط الذي يتمّ تنفيذه في عملية إعادة الاتصال requestAnimationFrame. يتم تنفيذ محتوى هذا الإجراء الخلفي بعد انتهاء تشغيل معالجات الأحداث، ولكن قبل إعادة احتساب التصميم وعمل التنسيق.

يمكن أن يستغرق إكمال عمليات الاستدعاء هذه وقتًا طويلاً إذا كان العمل الذي يتم تنفيذه فيها معقّدًا. إذا كنت تشتبه في أنّ قيم تأخُّر العرض العالية ناتجة عن عملك مع requestAnimationFrame، يمكنك استخدام بيانات LoAF التي تعرضها مكتبة web-vitals لتحديد هذه السيناريوهات:

onINP(({name, value, attribution}) => {
  const {presentationDelay} = attribution; // 543.1999999880791

  // Get the longest script from the last LoAF entry:
  const loaf = attribution.longAnimationFrameEntries.at(-1);
  const script = loaf?.scripts.sort((a, b) => b.duration - a.duration)[0];

  // Get the render start time and when style and layout began:
  const {renderStart} = loaf;         // 2489
  const {styleAndLayoutStart} = loaf; // 2989.5999999940395

  // Calculate the `requestAnimationFrame` callback's duration:
  const rafDuration = styleAndLayoutStart - renderStart; // 500.59999999403954

  if (script) {
    // Get attribution for the event handler that triggered
    // the long-running requestAnimationFrame callback:
    const {invokerType} = script;        // 'user-callback'
    const {invoker} = script;            // 'FrameRequestCallback'
    const {sourceURL} = script;          // 'https://example.com/app.js'
    const {sourceCharPosition} = script; // 83
    const {sourceFunctionName} = script; // 'update'
  }
});

إذا لاحظت أنّ جزءًا كبيرًا من وقت تأخّر العرض يتم إنفاقه في requestAnimationFrame طلب إعادة الاتصال، تأكَّد من أنّ العمل الذي يتم تنفيذه في طلبات إعادة الاتصال هذه يقتصر على تنفيذ العمل الذي يؤدي إلى إجراء تعديل فعلي على واجهة المستخدم. إنّ أي عمل آخر لا يؤثر في DOM أو لا يؤدي إلى تعديل الأنماط سيؤخّر بشكل غير ضروري عرض اللقطة التالية، لذا عليك الحذر.

الخاتمة

إنّ البيانات الميدانية هي أفضل مصدر للمعلومات التي يمكنك الاستفادة منها عندما يتعلق الأمر بفهم التفاعلات التي تتسبّب في مشاكل للمستخدمين الفعليين في المجال. من خلال الاعتماد على أدوات جمع البيانات الميدانية، مثل مكتبة JavaScript لبيانات الأداء الرئيسية على الويب (أو مقدّم RUM)، يمكنك التأكّد أكثر من التفاعلات التي تواجه مشاكل أكثر من غيرها، ثم الانتقال إلى إعادة إنتاج التفاعلات التي تواجه مشاكل في المختبر ثم العمل على حلّها.

الصورة الرئيسية من Unsplash، لأحد أعمال Federico Respini.