מדדים מותאמים אישית

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

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

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

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

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

ממשקי API למדידת מדדים מותאמים אישית

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

לדוגמה, אפשר לקבוע אם ה-thread הראשי חסום בגלל משימות JavaScript ממושכות על ידי הפעלת לולאה requestAnimationFrame וחישוב ההפרש בין כל פריים. אם הערך של 'דלתא' ארוך בהרבה מרמת הפריימים של המסך, אפשר לדווח על כך כמשימה ארוכה. עם זאת, לא מומלץ להשתמש בהאקים כאלה כי הם משפיעים על הביצועים בעצמם (למשל, על ידי שחיקה של הסוללה).

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

Performance Observer API

תמיכה בדפדפנים

  • Chrome: 52.
  • קצה: 79.
  • Firefox: 57.
  • Safari: 11.

מקור

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

אפשר להשתמש ב-PerformanceObserver כדי להירשם באופן פסיבי לאירועים שקשורים לביצועים. כך אפשר להפעיל קריאות חזרה (callbacks) של ממשקי API במהלך תקופות חוסר פעילות, כלומר בדרך כלל הן לא יפריעו לביצועים של הדף.

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

const po = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    // Log the entry and all associated details.
    console.log(entry.toJSON());
  }
});

po.observe({type: 'some-entry-type'});

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

צפייה ברשומות שכבר התרחשו

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

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

po.observe({
  type: 'some-entry-type',
  buffered: true,
});

ממשקי API מדור קודם לבדיקת ביצועים שכדאי להימנע מהם

לפני Performance Observer API, מפתחים יכלו לגשת לרשומי הביצועים באמצעות שלוש השיטות הבאות שהוגדרו באובייקט performance:

ממשקי ה-API האלה עדיין נתמכים, אבל לא מומלץ להשתמש בהם כי הם לא מאפשרים להאזין לאירועים של הוספת רשומות חדשות. בנוסף, הרבה ממשקי API חדשים (כמו largest-contentful-paint) לא נחשפים דרך האובייקט performance, אלא רק דרך PerformanceObserver.

אלא אם אתם צריכים תאימות ספציפית ל-Internet Explorer, מומלץ להימנע מהשימוש בשיטות האלה בקוד ולהשתמש ב-PerformanceObserver מעכשיו והלאה.

ממשק API של תזמון משתמש

תמיכה בדפדפנים

  • Chrome: 28.
  • Edge:‏ 12.
  • Firefox: 38.
  • Safari: 11.

מקור

User Timing API הוא ממשק API למדידת נתונים לשימוש כללי למדדים מבוססי-זמן. הוא מאפשר לסמן נקודות זמן באופן שרירותי, ולאחר מכן למדוד את משך הזמן בין הנקודות האלה.

// Record the time immediately before running a task.
performance.mark('myTask:start');
await doMyTask();

// Record the time immediately after running a task.
performance.mark('myTask:end');

// Measure the delta between the start and end of the task
performance.measure('myTask', 'myTask:start', 'myTask:end');

ממשקי API כמו Date.now() או performance.now() מעניקים לכם יכולות דומות, אבל היתרון של השימוש ב-User Timing API הוא שהוא משתלב היטב עם כלים לשיפור הביצועים. לדוגמה, כלי הפיתוח ל-Chrome מציגים מדידות של זמני משתמשים בחלונית 'ביצועים', וספקי ניתוח נתונים רבים גם יעקבו באופן אוטומטי אחרי כל המדידות שתבצעו וישלחו את נתוני משך הזמן לקצה העורפי של מערכת הניתוח שלהם.

כדי לדווח על מדידות של 'תזמון משתמש', אפשר להשתמש ב-PerformanceObserver ולהירשם למעקב אחרי רשומות מסוג measure:

// Create the performance observer.
const po = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    // Log the entry and all associated details.
    console.log(entry.toJSON());
  }
});

// Start listening for `measure` entries to be dispatched.
po.observe({type: 'measure', buffered: true});

Long Tasks API

תמיכה בדפדפן

  • Chrome: 58.
  • Edge:‏ 79.
  • Firefox: לא נתמך.
  • Safari: לא נתמך.

מקור

Long Tasks API עוזר לדעת מתי השרשור הראשי של הדפדפן חסום למשך זמן מספיק כדי להשפיע על קצב הפריימים או על זמן האחזור של הקלט. ה-API ידווח על משימות שתהליך הביצוע שלהן נמשך יותר מ-50 אלפיות שנייה.

בכל פעם שצריך להריץ קוד יקר או לטעון ולבצע סקריפטים גדולים, כדאי לעקוב אחרי הקוד כדי לראות אם הוא חוסם את ה-thread הראשי. למעשה, מדדים רבים ברמה גבוהה יותר מבוססים על Long Tasks API עצמו (כמו הזמן עד לפעילות מלאה (TTI) וסה"כ זמן החסימה (TBT)).

כדי לקבוע מתי מתבצעות משימות ארוכות, אפשר להשתמש ב-PerformanceObserver ולהירשם למעקב אחרי רשומות מסוג longtask:

// Create the performance observer.
const po = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    // Log the entry and all associated details.
    console.log(entry.toJSON());
  }
});

// Start listening for `longtask` entries to be dispatched.
po.observe({type: 'longtask', buffered: true});

Long Animation Frames API

תמיכה בדפדפנים

  • Chrome: 123.
  • Edge:‏ 123.
  • Firefox: לא נתמך.
  • Safari: לא נתמך.

מקור

Long Animation Frames API הוא גרסה חדשה של Long Tasks API, שמתבוננת בפריימים ארוכים – במקום במשימות ארוכות – של יותר מ-50 אלפיות השנייה. כך אפשר לטפל בחלק מהחסרונות של Long Tasks API, כולל שיוך מדויק יותר והיקף רחב יותר של עיכובים שעשויים להיות בעייתיים.

כדי לקבוע מתי מתרחשים פריימים ארוכים, אפשר להשתמש ב-PerformanceObserver ולהירשם למעקב אחרי רשומות מסוג long-animation-frame:

// Create the performance observer.
const po = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    // Log the entry and all associated details.
    console.log(entry.toJSON());
  }
});

// Start listening for `long-animation-frame` entries to be dispatched.
po.observe({type: 'long-animation-frame', buffered: true});

ממשק API לתזמון רכיבים

תמיכה בדפדפנים

  • Chrome: 77.
  • Edge:‏ 79.
  • Firefox: לא נתמך.
  • Safari: לא נתמך.

מקור

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

במקרים כאלה, צריך להשתמש ב-Element Timing API. ה-LCP API מבוסס למעשה על Element Timing API ומוסיף דיווח אוטומטי על הרכיב הגדול ביותר שמכיל תוכן, אבל אפשר גם לדווח על רכיבים אחרים על ידי הוספת המאפיין elementtiming אליהם באופן מפורש, ורישום של PerformanceObserver כדי לבחון את סוג הרשומה element.

<img elementtiming="hero-image" />
<p elementtiming="important-paragraph">This is text I care about.</p>
<!-- ... -->

<script>
  const po = new PerformanceObserver((entryList) => {
    for (const entry of entryList.getEntries()) {
      // Log the entry and all associated details.
      console.log(entry.toJSON());
    }
  });

  // Start listening for `element` entries to be dispatched.
  po.observe({type: 'element', buffered: true});
</script>

Event Timing API

תמיכה בדפדפנים

  • Chrome:‏ 76.
  • Edge:‏ 79.
  • Firefox: 89.
  • Safari: לא נתמך.

מקור

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

המדד INP מתקבל באמצעות Event Timing API. ה-API הזה חושף מספר חותמות זמן שמתרחשות במהלך מחזור החיים של האירוע, כולל:

  • startTime: השעה שבה הדפדפן מקבל את האירוע.
  • processingStart: השעה שבה הדפדפן יכול להתחיל לעבד גורמים מטפלים באירועים של האירוע.
  • processingEnd: הזמן שבו הדפדפן מסיים את ביצוע כל הקוד הסינכרוני שהופעל ממטפלי האירועים של האירוע הזה.
  • duration: הזמן (מעוגל ל-8 אלפיות השנייה מטעמי אבטחה) שחולף מהרגע שבו הדפדפן מקבל את האירוע ועד שהוא יכול לצייר את המסגרת הבאה אחרי שהוא מסיים להריץ את כל הקוד הסינכרוני שהופעל על ידי פונקציות הטיפול באירועים.

הדוגמה הבאה מראה איך להשתמש בערכים האלה כדי ליצור מדידות מותאמות אישית:

const po = new PerformanceObserver((entryList) => {
  // Get the last interaction observed:
  const entries = Array.from(entryList.getEntries()).forEach((entry) => {
    // Get various bits of interaction data:
    const inputDelay = entry.processingStart - entry.startTime;
    const processingTime = entry.processingEnd - entry.processingStart;
    const presentationDelay = entry.startTime + entry.duration - entry.processingEnd;
    const duration = entry.duration;
    const eventType = entry.name;
    const target = entry.target || "(not set)"

    console.log("----- INTERACTION -----");
    console.log(`Input delay (ms): ${inputDelay}`);
    console.log(`Event handler processing time (ms): ${processingTime}`);
    console.log(`Presentation delay (ms): ${presentationDelay}`);
    console.log(`Total event duration (ms): ${duration}`);
    console.log(`Event type: ${eventType}`);
    console.log(target);
  });
});

// A durationThreshold of 16ms is necessary to include more
// interactions, since the default is 104ms. The minimum
// durationThreshold is 16ms.
po.observe({type: 'event', buffered: true, durationThreshold: 16});

API לתזמון משאבים

תמיכה בדפדפנים

  • Chrome:‏ 29.
  • Edge:‏ 12.
  • Firefox: 35.
  • Safari: 11.

מקור

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

  • initiatorType: האופן שבו המשאב אוחזר: למשל מתג <script> או <link>, או קריאה ל-fetch().
  • nextHopProtocol: הפרוטוקול שמשמש לאחזור המשאב, למשל h2 או quic.
  • encodedBodySize/decodedBodySize]: הגודל של המשאב בפורמט המקודד או המפוענח שלו (בהתאמה)
  • transferSize: גודל המשאב שהוענק בפועל ברשת. כשמשאבים מתקבלים מהמטמון, הערך הזה יכול להיות קטן בהרבה מ-encodedBodySize, ובמקרים מסוימים הוא יכול להיות אפס (אם לא נדרשת אימות מחדש של המטמון).

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

בדוגמה הבאה מתועדים כל המשאבים שהדף ביקש, ומצוין אם כל משאב הושג מהמטמון או לא.

// Create the performance observer.
const po = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    // If transferSize is 0, the resource was fulfilled using the cache.
    console.log(entry.name, entry.transferSize === 0);
  }
});

// Start listening for `resource` entries to be dispatched.
po.observe({type: 'resource', buffered: true});

תמיכה בדפדפנים

  • Chrome: 57.
  • Edge:‏ 12.
  • Firefox: 58.
  • Safari: 15.

מקור

Navigation Timing API דומה ל-Resource Timing API, אבל הוא מדווח רק על בקשות ניווט. גם סוג הרשומה navigation דומה לסוג הרשומה resource, אבל הוא מכיל מידע נוסף שספציפי רק לבקשות ניווט (כמו הפעלת האירועים DOMContentLoaded ו-load).

מדד אחד שרבים מהמפתחים עוקבים אחריו כדי להבין את זמן התגובה של השרת (זמן עד בייט ראשון (TTFB)) זמין באמצעות Navigation Timing API – באופן ספציפי, חותמת הזמן responseStart של הרשומה.

// Create the performance observer.
const po = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    // If transferSize is 0, the resource was fulfilled using the cache.
    console.log('Time to first byte', entry.responseStart);
  }
});

// Start listening for `navigation` entries to be dispatched.
po.observe({type: 'navigation', buffered: true});

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

אפשר לקבוע את זמן ההפעלה של ה-service worker לבקשת ניווט מסוימת לפי ההפרש בין entry.responseStart ל-entry.workerStart.

// Create the performance observer.
const po = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    console.log('Service Worker startup time:',
        entry.responseStart - entry.workerStart);
  }
});

// Start listening for `navigation` entries to be dispatched.
po.observe({type: 'navigation', buffered: true});

Server Timing API

תמיכה בדפדפנים

  • Chrome: 65.
  • קצה: 79.
  • Firefox:‏ 61.
  • Safari: 16.4.

מקור

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

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

כדי לציין נתוני תזמון של שרת בתשובות, אפשר להשתמש בכותרת התגובה Server-Timing. נראה דוגמה.

HTTP/1.1 200 OK

Server-Timing: miss, db;dur=53, app;dur=47.2

לאחר מכן, תוכלו לקרוא את הנתונים האלה מהדפים שלכם גם ברשומות resource וגם ברשומות navigation מ-Resource Timing API ומ-Navigation Timing API.

// Create the performance observer.
const po = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    // Logs all server timing data for this response
    console.log('Server Timing', entry.serverTiming);
  }
});

// Start listening for `navigation` entries to be dispatched.
po.observe({type: 'navigation', buffered: true});