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

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

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

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

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

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

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

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

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

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

Cumulative Layout Shift (CLS)

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

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

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

הערך שדווח לגבי CLS משיעור ה-Lab (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 של 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.

סיכום

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

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

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

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