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

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

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

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

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

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

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

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

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

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

ממשק API של Performance Observer

תמיכה בדפדפן

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

מקור

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

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

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

ממשק API של Long Tasks

תמיכה בדפדפן

  • Chrome: 58.
  • קצה: 79.
  • Firefox: לא נתמך.
  • Safari: לא נתמך.

מקור

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

בכל פעם שצריך להריץ קוד יקר או לטעון ולהפעיל סקריפטים גדולים, כדאי לבדוק אם הקוד הזה חוסם את ה-thread הראשי. למעשה, הרבה מדדים ברמה גבוהה יותר מבוססים על ממשק ה-API של Long Tasks (כמו Time to Interactive (TTI) ו-Total Block Time (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});

ממשק API של מסגרות אנימציה ארוכות

תמיכה בדפדפן

  • Chrome: 123.
  • קצה: 123.
  • Firefox: לא נתמך.
  • Safari: לא נתמך.

מקור

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

כדי לקבוע מתי מתרחשות פריימים ארוכים, אפשר להשתמש ב-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.
  • קצה: 79.
  • Firefox: לא נתמך.
  • Safari: לא נתמך.

מקור

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

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

API לתזמון אירוע

תמיכה בדפדפן

  • Chrome: 76.
  • קצה: 79.
  • Firefox: 89.
  • Safari: לא נתמך.

מקור

המדד מהירות התגובה לאינטראקציה באתר (INP) בודק את רמת הרספונסיביות הכוללת של הדף לפי כל האינטראקציות מסוג קליקים, הקשה ומקלדת לאורך חיי הדף. לרוב, ה-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.
  • קצה: 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.
  • קצה: 12.
  • Firefox: 58.
  • Safari: 15.

מקור

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

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