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

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

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

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

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

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

تظهر بيانات CrUX في عدد من المناطق المختلفة، ويعتمد ذلك على نطاق المعلومات التي تبحث عنها. يمكن أن يوفّر تقرير CrUX بيانات حول مقياس INP ومؤشرات Core Web Vitals الأخرى لما يلي:

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

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

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

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

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

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

Browser Support

  • Chrome: 96.
  • Edge: 96.
  • Firefox: 144.
  • Safari: 26.2.

Source

يمكن استخدام الإصدار العادي من مكتبة 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);
});

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

التعمّق أكثر باستخدام إصدار تحديد المصدر في مكتبة web-vitals

إصدار تحديد المصدر من مكتبة 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.
كيفية ظهور البيانات من مكتبة web-vitals في وحدة التحكّم

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

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

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

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

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

واجهة برمجة التطبيقات Long Animation Frames API (LoAF)

Browser Support

  • Chrome: 123.
  • Edge: 123.
  • Firefox: not supported.
  • Safari: not supported.

Source

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

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

مفتاح عنصر إدخال 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.toSorted((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 لتتبُّع السبب الدقيق وراء تفاعل يتضمّن قيمًا عالية لمدة المعالجة، بما في ذلك:

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

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

مهلة الاستجابة للإدخال طويلة

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

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.toSorted((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.toSorted((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' يعنيان أنّ مهمة الحظر كانت من بعض الأعمال غير المتزامنة التي تم بدؤها في وقت سابق، وتم حلّها أو رفضها في وقت حاول فيه المستخدم التفاعل مع الصفحة، ما أدى إلى تأخير التفاعل.

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

مهلة عرض طويلة

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

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

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

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

عمليات مكلفة لتحديد الأنماط والتنسيقات

قد يكون التأخير الطويل في العرض ناتجًا عن إعادة احتساب الأنماط والتنسيق، وهما عمليتان مكلفّتان تنشآن عن عدد من الأسباب، بما في ذلك أدوات اختيار لغة CSS المعقّدة والأحجام الكبيرة لنموذج العناصر في المستند (DOM). يمكنك قياس مدة هذا العمل باستخدام توقيتات LoAF التي تظهر في مكتبة web-vitals:

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.toSorted((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.toSorted((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)، يمكنك أن تكون أكثر ثقة بشأن التفاعلات التي تسبّب أكبر قدر من المشاكل، ثم الانتقال إلى إعادة إنتاج التفاعلات التي تسبّب مشاكل في المختبر ثم إصلاحها.