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

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

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

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

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

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

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

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

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

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

Cumulative Layout Shift (CLS)

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

נבחן את הדוח הבא מ-PageSpeed Insights:

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

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

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

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

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

Largest Contentful Paint (LCP)

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

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

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

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

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

זיהוי האלמנט של מועמד ל-LCP

כדי לקבוע איזה רכיב של מועמד ל-LCP ב-JavaScript יהיה הרכיב LCP, אפשר להשתמש ב-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 מביא בחשבון את זמן האחזור המלא של אינטראקציה, כולל הזמן שלוקח להריץ פונקציות event listener רשומות, וכן הזמן שנדרש כדי לצבוע את הפריים הבא אחרי שכל האירועים שמאזינים מריצים. כלומר, ב-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 כוללת מודל שיוך (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);
onFID(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);
onFID(sendToGoogleAnalytics);
onINP(sendToGoogleAnalytics);

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

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

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

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

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

סיכום

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

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

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

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