ביצועי ניפוי באגים בשדה

איך משייכים את נתוני הביצועים למידע על ניפוי באגים כדי לזהות בעיות אצל משתמשים אמיתיים ולפתור אותן באמצעות ניתוח נתונים

Google מספקת שתי קטגוריות של כלים למדידת הביצועים ולניפוי באגים:

  • כלים של Lab: כלים כמו Lighthouse, שבהם הדף נטען בסביבה מדומה שיכולה לחקות תנאים שונים (לדוגמה, רשת איטית ומכשיר נייד ברמה נמוכה).
  • כלים שדה: כלים כמו הדוח על חוויית המשתמש ב-Chrome (CrUX), שמבוסס על נתונים מצטברים של משתמשים אמיתיים מ-Chrome. (שימו לב שנתוני השדה שמדווחים על ידי כלים כמו PageSpeed Insights ו-Search Console מגיעים מנתוני CrUX).

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

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

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

השאלה החשובה היא: איך אפשר לתעד מידע על ניפוי באגים של מדדי הליבה לבדיקת חוויית המשתמש באתר או של מדדי ביצועים אחרים ממשתמשים אמיתיים בשטח?

במאמר הזה נסביר בפירוט באילו ממשקי API אפשר להשתמש כדי לאסוף מידע נוסף על ניפוי באגים לכל אחד מהמדדים הנוכחיים של Core Web Vitals, ונציע רעיונות לאופן שבו אפשר לתעד את הנתונים האלה בכלי הניתוח הקיים שלכם.

ממשקי API לשיוך ולניפוי באגים

Cumulative Layout Shift (CLS)

מבין כל מדדי Core Web Vitals, CLS הוא אולי המדד שבו חשוב ביותר לאסוף מידע על ניפוי באגים בשטח. המדד CLS נמדד לאורך כל משך החיים של הדף, כך שהאופן שבו משתמש מבצע אינטראקציה עם הדף – המרחק שבו הוא גולל, הפריטים שהוא לוחץ עליהם וכו' – יכול להשפיע באופן משמעותי על כך שיהיו שינויי פריסה ועל אילו רכיבים יהיו שינויים.

בדוח הבא מ-PageSpeed Insights:

דוח PageSpeed Insights עם ערכים שונים של CLS
ב-PageSpeed Insights מוצגים גם נתונים מהשדה וגם נתוני מעבדה, אם הם זמינים, ויכול להיות שהם שונים

הערך שמדווח על CLS מהמעבדה (Lighthouse) בהשוואה ל-CLS מהשדה (נתוני CrUX) שונה למדי, וזה הגיוני אם מביאים בחשבון שיכול להיות שיש בדף הרבה תוכן אינטראקטיבי שלא נעשה בו שימוש בזמן הבדיקה ב-Lighthouse.

אבל גם אם אתם מבינים שאינטראקציה של משתמשים משפיעה על נתוני השדה, עדיין צריך לדעת אילו רכיבים בדף משתנים כדי לקבל ציון של 0.28 ב-75% העליונים. אפשר לעשות זאת באמצעות הממשק LayoutShiftAttribution.

אחזור שיוך של שינוי פריסה

הממשק LayoutShiftAttribution מוצג בכל רשומה layout-shift שמופיעה ב-Layout Instability API.

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

לפניכם קוד לדוגמה שמתעעד ביומן כל שינוי פריסה, וגם את הרכיבים שהשתנו:

new PerformanceObserver((list) => {
  for (const {value, startTime, sources} of list.getEntries()) {
    // Log the shift amount and other entry info.
    console.log('Layout shift:', {value, startTime});
    if (sources) {
      for (const {node, curRect, prevRect} of sources) {
        // Log the elements that shifted.
        console.log('  Shift source:', node, {curRect, prevRect});
      }
    }
  }
}).observe({type: 'layout-shift', buffered: true});

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

המטרה היא לא לזהות ולתקן כל שינוי בפריסה שמתרחש אצל כל משתמש, אלא לזהות את השינויים שמשפיעים על מספר המשתמשים הגדול ביותר, ולכן תורמים הכי הרבה ל-CLS של הדף ב-75% העליונים.

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

הקוד הבא מקבל רשימה של רשומות layout-shift שתרמו ל-CLS ומחזיר את רכיב המקור הגדול ביותר מהשינוי הגדול ביותר:

function getCLSDebugTarget(entries) {
  const largestEntry = entries.reduce((a, b) => {
    return a && a.value > b.value ? a : b;
  });
  if (largestEntry && largestEntry.sources && largestEntry.sources.length) {
    const largestSource = largestEntry.sources.reduce((a, b) => {
      return a.node && a.previousRect.width * a.previousRect.height >
          b.previousRect.width * b.previousRect.height ? a : b;
    });
    if (largestSource) {
      return largestSource.node;
    }
  }
}

אחרי שתזהו את הגורם המשמעותי ביותר לשינוי הגדול ביותר, תוכלו לדווח עליו בכלי הניתוח.

סביר להניח שהרכיב שתורם הכי הרבה ל-CLS בדף נתון ישתנה בהתאם למשתמש, אבל אם תצברו את הרכיבים האלה מכל המשתמשים, תוכלו ליצור רשימה של רכיבים נעים שמשפיעים על הכי הרבה משתמשים.

אחרי שתזהו את שורש הבעיה שגרמה לשינויים ברכיבים האלה ותטפלו בה, קוד Analytics יתחיל לדווח על שינויים קטנים יותר בתור השינויים 'הגרועים ביותר' בדפים שלכם. בסופו של דבר, כל השינויים שדווחו יהיו קטנים מספיק כדי שהדפים יהיו בטווח של סף הביצועים הטובים של 0.1.

מטא-נתונים נוספים שעשויים להיות שימושיים לתיעוד יחד עם רכיב המקור של השינוי הגדול ביותר הם:

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

Largest Contentful Paint (LCP)

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

חשוב לזכור שיכול להיות מאוד – למעשה, זה די נפוץ – שהרכיב האפשרי ל-LCP יהיה שונה ממשתמש למשתמש, גם באותו דף בדיוק.

יכולות להיות לכך כמה סיבות:

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

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

זיהוי הרכיב האפשרי ל-LCP

כדי לקבוע את הרכיב האפשרי ל-LCP ב-JavaScript, אפשר להשתמש ב-Largest Contentful Paint API, אותו ממשק API שבו משתמשים כדי לקבוע את ערך הזמן של LCP.

כשבודקים את הרשומות של largest-contentful-paint, אפשר לקבוע את הרכיב הנוכחי שעומד בקריטריונים ל-LCP על סמך המאפיין element של הרשומה האחרונה:

new PerformanceObserver((list) => {
  const entries = list.getEntries();
  const lastEntry = entries[entries.length - 1];

  console.log('LCP element:', lastEntry.element);
}).observe({type: 'largest-contentful-paint', buffered: true});

אחרי שתזהו את הרכיב האפשרי ל-LCP, תוכלו לשלוח אותו לכלי הניתוח יחד עם ערך המדד. בדומה ל-CLS, כך תוכלו לזהות אילו רכיבים חשוב לבצע להם אופטימיזציה קודם.

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

מהירות התגובה לאינטראקציה באתר (INP)

אלה הפרטים החשובים ביותר שצריך לתעד בשדה של INP:

  1. איזה רכיב היה מעורב באינטראקציה
  2. סוג האינטראקציה
  3. מתי האינטראקציה התרחשה

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

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

הקוד הבא מתעד ביומן את רכיב היעד ואת השעה של רשומת ה-INP.

function logINPDebugInfo(inpEntry) {
  console.log('INP target element:', inpEntry.target);
  console.log('INP interaction type:', inpEntry.name);
  console.log('INP time:', inpEntry.startTime);
}

שימו לב שהקוד הזה לא מראה איך לקבוע איזו רשומה של event היא הרשומה של INP, כי הלוגיקה הזו מורכבת יותר. עם זאת, בקטע הבא מוסבר איך לקבל את המידע הזה באמצעות ספריית JavaScript‏ web-vitals.

שימוש בספריית JavaScript של מדדי ה-Web Vitals

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

החל מגרסה 3, ספריית JavaScript של מדדי ה-web-vitals כוללת גרסת build של שיוך (Attribution) שמציגה את כל המידע הזה, וגם כמה אותות נוספים.

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

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

function sendToGoogleAnalytics({name, value, id, attribution}) {
  const eventParams = {
    metric_value: value,
    metric_id: id,
  }

  switch (name) {
    case 'CLS':
      eventParams.debug_target = attribution.largestShiftTarget;
      break;
    case 'LCP':
      eventParams.debug_target = attribution.element;
      break;
    case 'INP':
      eventParams.debug_target = attribution.interactionTarget;
      break;
  }

  // Assumes the global `gtag()` function exists, see:
  // https://developers.google.com/analytics/devguides/collection/ga4
  gtag('event', name, eventParams);
}

onCLS(sendToGoogleAnalytics);
onLCP(sendToGoogleAnalytics);
onINP(sendToGoogleAnalytics);

הקוד הזה ספציפי ל-Google Analytics, אבל הרעיון הכללי צריך להתאים גם לכלים אחרים לניתוח נתונים.

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

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

גרסת build של שיוך מסוג web-vitals חושפת מידע נוסף על השיוך, כפי שמוצג בדוגמה הבאה ל-INP:

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

function sendToGoogleAnalytics({name, value, id, attribution}) {
  const eventParams = {
    metric_value: value,
    metric_id: id,
  }

  switch (name) {
    case 'INP':
      eventParams.debug_target = attribution.interactionTarget;
      eventParams.debug_type = attribution.interactionType;
      eventParams.debug_time = attribution.interactionTime;
      eventParams.debug_load_state = attribution.loadState;
      eventParams.debug_interaction_delay = Math.round(attribution.inputDelay);
      eventParams.debug_processing_duration = Math.round(attribution.processingDuration);
      eventParams.debug_presentation_delay =  Math.round(attribution.presentationDelay);
      break;

    // Additional metric logic...
  }

  // Assumes the global `gtag()` function exists, see:
  // https://developers.google.com/analytics/devguides/collection/ga4
  gtag('event', name, eventParams);
}

onCLS(sendToGoogleAnalytics);
onLCP(sendToGoogleAnalytics);
onINP(sendToGoogleAnalytics);

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

דיווח על הנתונים והצגה חזותית שלהם

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

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

לגבי GA4, אפשר לעיין במאמר הייעודי בנושא איך שולחים שאילתות לגבי הנתונים ומציגים אותם באופן חזותי באמצעות BigQuery.

סיכום

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

אם אתם רק מתחילים למדוד את הביצועים ואתם כבר משתמשים ב-Google Analytics, כדאי להתחיל להשתמש בכלי הדוחות של מדדי Web Vitals, כי הוא כבר תומך בדיווח על פרטי ניפוי באגים של מדדי Core Web Vitals.

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

לסיום, אם לדעתכם יש פערים ביכולת לנפות באגים במדדים האלה בגלל תכונות או מידע חסרים בממשקי ה-API עצמם, תוכלו לשלוח משוב לכתובת web-vitals-feedback@googlegroups.com.