חיפוש אינטראקציות איטיות בשדה

כאן מוסבר איך למצוא אינטראקציות איטיות בנתוני השדה של האתר, כדי שתוכלו לזהות הזדמנויות לשיפור המדד 'מאינטראקציה ועד הצגת התגובה'.

נתונים מהשטח הם נתונים שמראים לכם מהי חוויית המשתמשים בפועל באתר שלכם. הוא מאפשר לזהות בעיות שלא ניתן למצוא רק בנתונים של סביבת ניסוי. בנוגע למהירות התגובה לאינטראקציה באתר (INP), נתוני שדה חיוניים לזיהוי אינטראקציות איטיות, ומספקים רמזים חשובים שיעזרו לכם לתקן אותן.

במדריך הזה נסביר איך להעריך במהירות את מדד ה-INP באתר באמצעות נתוני שטח מהדוח לגבי חוויית המשתמש ב-Chrome‏ (CrUX), כדי לבדוק אם יש באתר בעיות שקשורות למדד ה-INP. בהמשך נסביר איך להשתמש בגרסת השיוך של ספריית web-vitals JavaScript – ובתובנות החדשות שהיא מספקת מ-Long Animation Frames API (LoAF) – כדי לאסוף ולפרש נתונים מהשטח לגבי אינטראקציות איטיות באתר.

כדאי להתחיל עם CrUX כדי להעריך את מדד ה-INP של האתר

אם אתם לא אוספים נתוני שטח מהמשתמשים באתר, יכול להיות ש-CrUX הוא נקודת התחלה טובה. ב-CrUX נאספים נתוני שטח ממשתמשי Chrome אמיתיים שהביעו את הסכמתם לשליחת נתוני טלמטריה.

נתוני CrUX מוצגים באזורים שונים, בהתאם להיקף המידע שאתם מחפשים. דוח CrUX יכול לספק נתונים על INP ומדדים בסיסיים אחרים של חוויית המשתמש לגבי:

  • דפים בודדים ומקורות שלמים באמצעות PageSpeed Insights.
  • סוגי הדפים. לדוגמה, להרבה אתרי מסחר אלקטרוני יש סוגים של דף פרטי מוצר ודף רשימת מוצרים. אפשר לקבל נתוני CrUX לגבי סוגים ייחודיים של דפים ב-Search Console.

כנקודת התחלה, אפשר להזין את כתובת האתר שלכם ב-PageSpeed Insights. אחרי שמזינים את כתובת ה-URL, מוצגים נתוני השדה שלה – אם הם זמינים – לגבי כמה מדדים, כולל INP. אפשר גם להשתמש במתגים כדי לבדוק את ערכי ה-INP למאפיינים של ניידים ומחשבים.

נתוני שטח כפי שמוצגים ב-CrUX בכלי PageSpeed Insights, עם המדדים LCP,‏ INP ו-CLS בשלושת המדדים הבסיסיים של חוויית המשתמש באתר, ועם המדדים TTFB ו-FCP כמדדים לאבחון, ו-FID כמדד שהוצא משימוש.
קריאה של נתוני CrUX כפי שמוצגים ב-PageSpeed Insights. בדוגמה הזו, ערך ה-INP של דף האינטרנט שצוין זקוק לשיפור.

הנתונים האלה שימושיים כי הם מאפשרים לכם לדעת אם יש בעיה. עם זאת, CrUX לא יכול להגיד לכם מה גורם לבעיות. יש הרבה פתרונות לניטור משתמשים אמיתיים (RUM) שיעזרו לכם לאסוף נתוני שדות מהמשתמשים באתר, כדי לענות על השאלה הזו. אפשרות נוספת היא לאסוף את נתוני השדות בעצמכם באמצעות ספריית JavaScript של מדדי ליבה לאינטרנט.

איסוף נתונים מהשטח באמצעות ספריית JavaScript‏ web-vitals

web-vitals ספריית JavaScript היא סקריפט שאפשר לטעון באתר כדי לאסוף נתוני שדות מהמשתמשים באתר. אפשר להשתמש בו כדי לתעד מספר מדדים, כולל INP בדפדפנים שתומכים בו.

Browser Support

  • Chrome: 96.
  • Edge: 96.
  • Firefox Technology Preview: supported.
  • Safari: not supported.

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);
});

עם זאת, הנתונים האלה לבדם לא מספקים מידע רב מעבר למה שמופיע ב-CrUX. כאן נכנס לתמונה ייחוס הבנייה של ספריית Web Vitals.

שימוש ב-attribution build של ספריית 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 של הדף עצמו, תהליך בניית השיוך מספק הרבה נתונים שיכולים לעזור לכם להבין את הסיבות לאינטראקציות איטיות, כולל באיזה חלק של האינטראקציה כדאי להתמקד. הנתונים שנוצרים במודל יכולים לעזור לכם לענות על שאלות חשובות, למשל:

  • "האם המשתמש יצר אינטראקציה עם הדף בזמן הטעינה שלו?"
  • "האם הפעלתם את מטפלי האירועים של האינטראקציה במשך זמן רב?"
  • "האם ההפעלה של הקוד של handler האירוע של האינטראקציה התעכבה? אם כן, מה עוד קרה בשרשור הראשי באותו זמן?"
  • "האם האינטראקציה גרמה לעבודת רינדור רבה שעיכבה את הצגת הפריים הבא?"

בטבלה הבאה מוצגים חלק מנתוני השיוך הבסיסיים שאפשר לקבל מהספרייה. הנתונים האלה יכולים לעזור לכם להבין מהן הסיבות העיקריות לאינטראקציות איטיות באתר שלכם:

attribution מפתח אובייקט נתונים
interactionTarget סלקטור ב-CSS שמצביע על הרכיב שיצר את ערך ה-INP של הדף – לדוגמה, button#save.
interactionType סוג האינטראקציה, מתוך קליקים, הקשות או קלט מהמקלדת.
inputDelay* השהיה לאחר קלט של האינטראקציה.
processingDuration* הזמן שחלף מהרגע שבו התחיל לפעול מאזין האירועים הראשון בתגובה לאינטראקציה של המשתמש ועד לסיום העיבוד של כל מאזיני האירועים.
presentationDelay* השהיית ההצגה של האינטראקציה, שמתרחשת מהרגע שבו הגורמים המטפלים באירועים מסיימים את הפעולה ועד שהפריים הבא מצויר.
longAnimationFrameEntries* רשומות מ-LoAF שמשויכות לאינטראקציה. מידע נוסף מופיע בקטע הבא.
*חדש בגרסה 4

החל מגרסה 4 של ספריית web-vitals, אפשר לקבל תובנות מעמיקות יותר לגבי אינטראקציות בעייתיות באמצעות הנתונים שהיא מספקת עם פירוט של שלבי INP (זמן השהיה של הקלט, משך העיבוד וזמן ההצגה) ו-Long Animation Frames API (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 נתונים
duration משך הזמן של פריים האנימציה הארוך, עד לסיום הפריסה, לא כולל ציור והרכבה.
blockingDuration הזמן הכולל בפריים שבו הדפדפן לא הצליח להגיב במהירות בגלל משימות ארוכות. זמן החסימה הזה יכול לכלול משימות ארוכות שמריצות JavaScript, וגם כל משימת עיבוד ארוכה שמתבצעת לאחר מכן בפריים.
firstUIEventTimestamp חותמת הזמן של מועד הוספת האירוע לתור במהלך המסגרת. המדד הזה שימושי כדי להבין מתי מתחיל העיכוב בקלט של אינטראקציה.
startTime חותמת הזמן של תחילת המסגרת.
renderStart השעה שבה התחילה עבודת הרינדור של הפריים. ההסכמה הזו כוללת קריאות חוזרות (callback) של requestAnimationFrame (וקריאות חוזרות של ResizeObserver אם רלוונטי), אבל יכול להיות שהיא תתקבל לפני שיתחיל עיבוד של סגנון או פריסה.
styleAndLayoutStart כשמתבצעת עבודה על הסגנון או הפריסה בתוך המסגרת. יכול לעזור להבין את משך העבודה על הסגנון או הפריסה, כשמשקללים חותמות זמן אחרות שזמינות.
scripts מערך של פריטים שמכילים פרטי שיוך של סקריפטים שמשפיעים על מדד ה-INP של הדף.
תצוגה חזותית של פריים אנימציה ארוך לפי מודל LoAF.
תרשים של התזמונים של פריים אנימציה ארוך לפי LoAF API (מינוס blockingDuration).

כל המידע הזה יכול לתת לכם תמונה מלאה לגבי הגורמים שגורמים לאינטראקציה איטית, אבל המערך scripts שמופיע ברשומות של LoAF הוא בעל חשיבות מיוחדת:

מפתח אובייקט של שיוך (Attribution) סקריפט נתונים
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 כדי לזהות כמה מהגורמים לאינטראקציות איטיות.

משך עיבוד ארוך

משך העיבוד של אינטראקציה הוא הזמן שנדרש להפעלת קריאות חוזרות (callback) של מטפל האירועים הרשום של האינטראקציה עד לסיום, וכל דבר אחר שעשוי לקרות ביניהן. ספריית 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 כדי לאתר את הסיבה המדויקת לאינטראקציה עם ערכים גבוהים של משך העיבוד, כולל:

  • האלמנט וה-event listener הרשום שלו.
  • קובץ הסקריפט – והמיקום של התו בתוכו – שמכיל את הקוד של הגורם המטפל באירועים שפועל לאורך זמן.
  • שם הפונקציה.

סוג הנתונים הזה הוא בעל ערך רב. לא צריך יותר להתאמץ כדי לגלות בדיוק איזו אינטראקציה – או איזו פונקציית handler של אירוע – אחראית לערכים גבוהים של משך העיבוד. בנוסף, סקריפטים של צד שלישי יכולים לרשום לעצמם מטפלי אירועים, כך שתוכלו לקבוע אם הקוד שלכם הוא זה שאחראי לכך. אם יש לכם שליטה על הקוד, כדאי לכם לבדוק איך מבצעים אופטימיזציה של משימות ארוכות.

השהיות ארוכות לאחר קלט

למרות שמטפלי אירועים שפועלים לאורך זמן הם נפוצים, יש חלקים אחרים באינטראקציה שכדאי לקחת בחשבון. חלק אחד מתרחש לפני משך העיבוד, והוא נקרא השהיית קלט. זהו הזמן שחולף מהרגע שבו המשתמש יוזם את האינטראקציה ועד לרגע שבו מתחילות לפעול פונקציות ה-callback של המטפל באירועים. זה קורה כשהשרשור הראשי כבר מעבד משימה אחרת. השיוך שנוצר על ידי הספרייה web-vitals יכול להראות לכם את משך הזמן של השהיית הקלט לאינטראקציה:

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

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

אם אתם מבחינים שחלק מהאינטראקציות כוללות עיכובים ארוכים בקלט, תצטרכו להבין מה קרה בדף בזמן האינטראקציה שגרם לעיכוב הארוך בקלט. בדרך כלל, הבעיה היא שהאינטראקציה התרחשה בזמן שהדף נטען או אחרי הטעינה.

האם זה קרה במהלך טעינת הדף?

ה-thread הראשי הוא בדרך כלל העמוס ביותר בזמן טעינת הדף. במהלך הזמן הזה, כל מיני משימות מתווספות לתור ומעובדות, ואם המשתמש מנסה לבצע אינטראקציה עם הדף בזמן שכל העבודה הזו מתבצעת, האינטראקציה עלולה להתעכב. דפים שטוענים הרבה 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, או אפילו קריאות חוזרות (callback) לאירועים שהוכנסו לתור להפעלה מוקדמת יותר, ועדיין נמצאות בתהליך.

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'
  }
});

כמו במקרים של פתרון בעיות שקשורות לערכים גבוהים של משך העיבוד, עיכובים גבוהים בקלט בגלל הסיבות שצוינו קודם יספקו לכם נתוני שיוך מפורטים של הסקריפט. עם זאת, מה שכן משתנה הוא סוג הקריאה (invoker), בהתאם לאופי העבודה שגרמה לעיכוב באינטראקציה:

  • '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 כדי לחשב את משך העבודה בצורה מדויקת. לשם כך, צריך לקבוע את זמן הסיום של הפריים ולהחסיר ממנו את חותמת הזמן של ההתחלה של עבודת הסגנון והפריסה.

קריאות חוזרות (callback) ממושכות של requestAnimationFrame

סיבה אפשרית לעיכובים ארוכים בהצגה היא עבודה מוגזמת שמתבצעת בקריאה חוזרת (callback) של 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קריאה חוזרת (callback), ודאו שהעבודה שאתם מבצעים בקריאות החוזרות האלה מוגבלת לביצוע עבודה שמובילה לעדכון בפועל של ממשק המשתמש. כל עבודה אחרת שלא משפיעה על ה-DOM או על עדכון הסגנונות תגרום לעיכוב מיותר בציור הפריים הבא, אז צריך להיזהר!

סיכום

נתוני השדות הם המקור הכי טוב למידע שיעזור לכם להבין אילו אינטראקציות בעייתיות למשתמשים אמיתיים בשטח. אם מסתמכים על כלים לאיסוף נתונים בשטח, כמו ספריית JavaScript של מדדי ליבה לאתרים (או ספק RUM), אפשר להיות בטוחים יותר לגבי האינטראקציות הבעייתיות ביותר, ואז לעבור לשחזור אינטראקציות בעייתיות במעבדה ולתקן אותן.

תמונה ראשית (Hero) מ-Unsplash, מאת Federico Respini.