כאן מוסבר איך לשייך את נתוני הביצועים באמצעות מידע על תוצאות ניפוי באגים, כדי לזהות ולפתור בעיות של משתמשים אמיתיים בניתוח נתונים
Google מספקת שתי קטגוריות של כלים למדידת ביצועים ולניפוי באגים:
- כלי שיעור Lab: כלים כמו Lighthouse, שבו הדף נטען בסביבה מדומה, שיכולה לחקות תנאים שונים (למשל רשת איטית ומכשיר נייד לא מעשי).
- כלי שדות: כלים כמו דוח חוויית המשתמש ב-Chrome (CrUX), המבוסס על נתונים נצברים ממשתמשים אמיתיים מ-Chrome. (שימו לב שנתוני השטח שמדווחים על ידי כלים כמו PageSpeed Insights ו-Search Console מגיעים מנתוני CrUX).
הכלים בשטח מספקים נתונים מדויקים יותר, כלומר נתונים שמייצגים בפועל את החוויה של המשתמשים, אבל בדרך כלל הכלים האלה עוזרים לכם לזהות ולפתור בעיות בצורה טובה יותר.
נתוני CrUX מייצגים יותר את הביצועים האמיתיים של הדף שלך, אבל הכרת הציונים שלך ב-CrUX לא תעזור לך להבין איך לשפר את הביצועים.
לעומת זאת, Lighthouse יזהה בעיות ויספק הצעות ספציפיות לשיפור. עם זאת, מערכת Lighthouse תציע הצעות רק לגבי בעיות בביצועים שהיא מוצאת בזמן טעינת הדף. המערכת לא מזהה בעיות שמתגלות רק כתוצאה מאינטראקציה של המשתמש כמו גלילה או לחיצה על לחצנים בדף.
זו שאלה חשובה: איך אפשר לתעד מידע על תוצאות ניפוי הבאגים במדדי הליבה לבדיקת חוויית המשתמש באתר או במדדי ביצועים אחרים ממשתמשים אמיתיים בשטח?
בפוסט הזה מוסבר בפירוט באילו ממשקי API אפשר להשתמש כדי לאסוף מידע נוסף על תוצאות ניפוי הבאגים בכל אחד מהמדדים הקיימים של מדדי הליבה לבדיקת חוויית המשתמש באתר. בנוסף, תקבלו רעיונות לאיסוף הנתונים האלה בכלי הקיים לניתוח נתונים.
ממשקי API לשיוך ולניפוי באגים
CLS
מבין כל המדדים של מדדי ליבה לבדיקת חוויית המשתמש באתר, CLS הוא אולי המדד החשוב ביותר לצורך איסוף המידע על תוצאות ניפוי הבאגים בשדה. מדד CLS נמדד לאורך כל משך החיים של הדף, כך שהאופן שבו המשתמש מקיים אינטראקציה עם הדף – כמה רחוק הוא גוללים, על מה הוא לוחץ וכו' – יכול להשפיע בצורה משמעותית על התנודות בפריסה ועל האלמנטים שנעים.
עיינו בדוח הבא של PageSpeed Insights:
הערך שדווח לגבי CLS מהשיעור ה-Lab (Lighthouse) בהשוואה ל-CLS מהשדה (נתוני CrUX) שונה למדי, וזה הגיוני אם חושבים שהדף עשוי להכיל הרבה תוכן אינטראקטיבי שאינו נמצא בשימוש כשנבדק ב-Lighthouse.
אבל גם אם אתם מבינים שאינטראקציה של משתמש משפיעה על נתוני השדות, עדיין עליכם לדעת אילו רכיבים בדף משתנים כדי לקבל ציון של 0.3 במאון ה-75.
הממשק LayoutShiftAttribution מאפשר זאת.
קבלת שיוך (Attribution) לשינוי פריסה
הממשק 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!
מטא-נתונים נוספים שאולי כדאי לתעד יחד עם רכיב המקור הכי גדול (Shift) הוא:
- מועד השינוי הגדול ביותר
- נתיב כתובת ה-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 בכך שקטעי המידע השימושיים ביותר לצילום בשטח הם:
- עם איזה רכיב בוצעה אינטראקציה
- למה סוג האינטראקציה הזו
- מתי האינטראקציה התרחשה
בדומה ל-FID, אחד מהגורמים העיקריים לאינטראקציות איטיות הוא פרוטוקול Thread ראשי חסום. מצב כזה יכול להיות נפוץ במהלך הטעינה של 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 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 '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 הקיימים של Performance ובספריית web-vitals
כדי לקבל מידע על תוצאות ניפוי הבאגים, וכך לאבחן את הביצועים על סמך ביקורים של משתמשים בפועל בשטח. המדריך הזה מתמקד בדוח המדדים הבסיסיים של חוויית המשתמש (Core Web Vitals), אבל המושגים רלוונטיים גם לניפוי באגים בכל מדד ביצועים שניתן למדוד ב-JavaScript.
אם רק התחלתם למדוד ביצועים, ואתם כבר משתמשים ב-Google Analytics, מומלץ להשתמש בכלי הדוח Web Vitals, כי הוא כבר תומך בדיווח על תוצאות ניפוי באגים במדדים הבסיסיים של חוויית המשתמש באתר.
אם אתם ספקים של ניתוח נתונים ואתם רוצים לשפר את המוצרים שלכם ולספק למשתמשים יותר מידע על תוצאות ניפוי הבאגים, קחו בחשבון כמה מהשיטות שמתוארות כאן, אבל אל תגבילו רק רק לרעיונות שמוצגים כאן. הפוסט הזה מיועד להיות רלוונטי באופן כללי לכל כלי הניתוח. עם זאת, סביר להניח שכלים נפרדים לניתוח נתונים יוכלו (וצריכים) לקלוט ולדווח על מידע רב עוד יותר על ניפוי באגים.
לסיום, אם אתם חושבים שיש פערים ביכולת שלכם לנפות באגים במדדים האלה בגלל שחסרות תכונות או מידע בממשקי ה-API עצמם, שלחו את המשוב לכתובת web-vitals-feedback@googlegroups.com.