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

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

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

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

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

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

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

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

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

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

CLS

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

כדאי לעיין בדוח הבא של PageSpeed Insights:

דוח PageSpeed Insights עם ערכי CLS שונים

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

אבל גם אם אתם מבינים שאינטראקציה של משתמש משפיעה על נתוני השדות, עדיין צריך לדעת אילו רכיבים בדף משתנים כדי לקבל ציון של 0.3 באחוזון ה-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 באופן דינמי, כמו Single Page Applications).

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, שיכולים לעזור לכם לקבוע אילו שלבי אופטימיזציה ספציפיים רלוונטיים לאתר.

FID

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

לדוגמה, אפליקציות JavaScript רבות שתומכות ברינדור בצד השרת (SSR) יעבירו קוד HTML סטטי למסך לפני שהוא אינטראקטיבי לקלט של משתמשים, כלומר לפני שה-JavaScript שנדרש כדי להפוך את התוכן האינטראקטיבי מסיים את הטעינה.

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

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

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

הקוד הבא מתעד רשומות ורישומים של first-input, גם אם הקלט הראשון התרחש לפני סיום האירוע DOMContentLoaded וגם אם לא:

new PerformanceObserver((list) => {
  const fidEntry = list.getEntries()[0];
  const navEntry = performance.getEntriesByType('navigation')[0];
  const wasFIDBeforeDCL =
    fidEntry.startTime < navEntry.domContentLoadedEventStart;

  console.log('FID occurred before DOMContentLoaded:', wasFIDBeforeDCL);
}).observe({type: 'first-input', buffered: true});

זיהוי רכיב היעד של FID וסוג האירוע

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

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

כדי למצוא את סוג האינטראקציה ואת הרכיב שמשויכים לאירוע הקלט הראשון, אפשר להפנות למאפיינים target ו-name של הרשומה first-input:

new PerformanceObserver((list) => {
  const fidEntry = list.getEntries()[0];

  console.log('FID target element:', fidEntry.target);
  console.log('FID interaction type:', fidEntry.name);
}).observe({type: 'first-input', buffered: true});

INP

INP דומה מאוד ל-FID בכך שקטעי המידע השימושיים ביותר לתיעוד בשטח הם:

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

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

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

מאחר ש-INP ו-FID מבוססים שניהם על Event Timing API, הדרך שבה קובעים את המידע הזה ב-JavaScript דומה מאוד לדוגמה הקודמת. הקוד הבא מתעד את רכיב היעד והזמן (ביחס ל-DOMContentLoaded) של רשומת ה-INP.

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

  const navEntry = performance.getEntriesByType('navigation')[0];
  const wasINPBeforeDCL =
    inpEntry.startTime < navEntry.domContentLoadedEventStart;

  console.log('INP occurred before DCL:', wasINPBeforeDCL);
}

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

שימוש עם ספריית JavaScript של אתרי אינטרנט

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

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

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

import {onCLS, onFID, 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 'FID':
    case 'INP':
      eventParams.debug_target = attribution.eventTarget;
      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, כדאי לאסוף את סוג האינטראקציה, את השעה וגם את הרכיב שאיתו מתקיימת אינטראקציה. ה-build של השיוך (Attribution) web-vitals חושף את כל המידע הזה, כמו בדוגמה הבאה:

import {onCLS, onFID, 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.eventTarget;
      eventParams.debug_type = attribution.eventType;
      eventParams.debug_time = attribution.eventTime;
      eventParams.debug_load_state = attribution.loadState;
      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 כדי לקבל מידע על תוצאות ניפוי באגים, שבעזרתו אבחון הביצועים על סמך ביקורים של משתמשים בפועל בשטח. המדריך הזה מתמקד במדדי הליבה לבדיקת חוויית המשתמש באתר, אבל הם רלוונטיים גם לניפוי באגים בכל מדד ביצועים שניתן למדוד ב-JavaScript.

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

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

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