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

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

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

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

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

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

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

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

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

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

Performance Observer API

Browser Support

  • Chrome: 52.
  • Edge: 79.
  • Firefox: 57.
  • Safari: 11.

Source

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

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

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

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

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

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

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

User Timing API

Browser Support

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

Source

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

Browser Support

  • Chrome: 58.
  • Edge: 79.
  • Firefox: not supported.
  • Safari: not supported.

Source

Long Tasks API עוזר לדעת מתי ה-thread הראשי של הדפדפן חסום למשך זמן מספיק כדי להשפיע על קצב הפריימים או על זמן האחזור של הקלט. ה-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

Browser Support

  • Chrome: 123.
  • Edge: 123.
  • Firefox: not supported.
  • Safari: not supported.

Source

Long Animation Frames API הוא איטרציה חדשה של Long Tasks API, שמתמקדת בפריימים ארוכים – ולא במשימות ארוכות – של יותר מ-50 מילי-שניות. השימוש ב-API הזה מאפשר לטפל בחלק מהחסרונות של 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});

Element Timing API

Browser Support

  • Chrome: 77.
  • Edge: 79.
  • Firefox: not supported.
  • Safari: not supported.

Source

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

במקרים כאלה, כדאי להשתמש ב-Element Timing API. ה-API של LCP מבוסס למעשה על ה-API של Element Timing, והוא מוסיף דיווח אוטומטי על הרכיב הגדול ביותר שכולל תוכן. אבל אפשר גם לדווח על רכיבים אחרים על ידי הוספת המאפיין 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

Browser Support

  • Chrome: 76.
  • Edge: 79.
  • Firefox: 89.
  • Safari: 26.2.

Source

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

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

  • startTime: השעה שבה הדפדפן מקבל את האירוע.
  • processingStart: השעה שבה הדפדפן יכול להתחיל לעבד את הגורמים שמטפלים באירועים של האירוע.
  • processingEnd: הזמן שבו הדפדפן מסיים להריץ את כל הקוד הסינכרוני שהופעל מתוך handlers של אירועים עבור האירוע הזה.
  • 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});

Resource Timing API

Browser Support

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

Source

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});

Browser Support

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

Source

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});

מדד נוסף שעשוי לעניין מפתחים שמשתמשים ב-service worker הוא זמן ההפעלה של service worker לבקשות ניווט. זהו משך הזמן שחולף עד שהדפדפן מתחיל את השרשור של 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

Browser Support

  • Chrome: 65.
  • Edge: 79.
  • Firefox: 61.
  • Safari: 16.4.

Source

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 מתוך ממשקי ה-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});