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

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

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

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

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

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

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

ממשק API של Performanceצפייה

תמיכה בדפדפן

  • 52
  • 79
  • 57
  • 11

מקור

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

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

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

// Catch errors that some browsers throw when using the new `type` option.
// https://bugs.webkit.org/show_bug.cgi?id=209216
try {
  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'});
} catch (e) {
  // Do nothing if the browser doesn't support this API.
}

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

לזהות רשומות שכבר התרחשו

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

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

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

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

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

בנוסף, הרבה ממשקי API חדשים (כמו משימות ארוכות) לא נחשפים על ידי האובייקט performance, אלא רק על ידי PerformanceObserver. לכן, אלא אם אתם צריכים תאימות ספציפית ל-Internet Explorer, עדיף להימנע מהשיטות האלה בקוד ולהשתמש ב-PerformanceObserver מעכשיו והלאה.

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

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:

// Catch errors some browsers throw when using the new `type` option.
// https://bugs.webkit.org/show_bug.cgi?id=209216
try {
  // 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.
  po.observe({type: 'measure', buffered: true});
} catch (e) {
  // Do nothing if the browser doesn't support this API.
}

ממשק API של משימות ארוכות

תמיכה בדפדפן

  • 58
  • 79
  • x
  • x

מקור

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

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

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

// Catch errors since some browsers throw when using the new `type` option.
// https://bugs.webkit.org/show_bug.cgi?id=209216
try {
  // 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});
} catch (e) {
  // Do nothing if the browser doesn't support this API.
}

Element Timing API

תמיכה בדפדפן

  • 77
  • 79
  • x
  • x

מקור

בעזרת המדד 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>
// Catch errors since some browsers throw when using the new `type` option.
// https://bugs.webkit.org/show_bug.cgi?id=209216
try {
  // Create the performance observer.
  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});
} catch (e) {
  // Do nothing if the browser doesn't support this API.
}
</script>

ממשק API של תזמון אירועים

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

הדבר אפשרי באמצעות Event Timing API, שבנוסף למדידת FID, חושף גם מספר חותמות זמן במחזור החיים של האירוע, כולל:

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

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

// Catch errors some browsers throw when using the new `type` option.
// https://bugs.webkit.org/show_bug.cgi?id=209216
try {
  const po = new PerformanceObserver((entryList) => {
    const firstInput = entryList.getEntries()[0];

    // Measure First Input Delay (FID).
    const firstInputDelay = firstInput.processingStart - firstInput.startTime;

    // Measure the time it takes to run all event handlers
    // Doesn't include work scheduled asynchronously using methods like
    // `requestAnimationFrame()` or `setTimeout()`.
    const firstInputProcessingTime = firstInput.processingEnd - firstInput.processingStart;

    // Measure the entire duration of the event, from when input is received by
    // the browser until the next frame can be painted after processing all
    // event handlers.
    // Doesn't include work scheduled asynchronously using
    // `requestAnimationFrame()` or `setTimeout()`.
    // For security reasons, this value is rounded to the nearest 8 ms.
    const firstInputDuration = firstInput.duration;

    // Log these values to the console.
    console.log({
      firstInputDelay,
      firstInputProcessingTime,
      firstInputDuration,
    });
  });

  po.observe({type: 'first-input', buffered: true});
} catch (error) {
  // Do nothing if the browser doesn't support this API.
}

Resource Timing API

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

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

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

בדוגמה הבאה מתועדים כל המשאבים שהדף נדרש, ומציינת אם בוצע מילוי של כל משאב באמצעות המטמון:

// Catch errors some browsers throw when using the new `type` option.
// https://bugs.webkit.org/show_bug.cgi?id=209216
try {
  // Create the performance observer.
  const po = new PerformanceObserver((list) => {
    for (const entry of list.getEntries()) {
      // If transferSize is 0, the resource was fulfilled via the cache.
      console.log(entry.name, entry.transferSize === 0);
    }
  });
  // Start listening for `resource` entries to be dispatched.
  po.observe({type: 'resource', buffered: true});
} catch (e) {
  // Do nothing if the browser doesn't support this API.
}

תמיכה בדפדפן

  • 57
  • 12
  • 58
  • 15

מקור

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

מדד אחד שמפתחים רבים עוקבים אחריו כדי להבין את זמן התגובה של השרת, Time to First Byte (TTFB), שזמין דרך חותמת הזמן responseStart ב-Navigation Timing API.

// Catch errors since  browsers throw when using the new `type` option.
// https://bugs.webkit.org/show_bug.cgi?id=209216
try {
  // 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});
} catch (e) {
  // Do nothing if the browser doesn't support this API.
}

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

ניתן לקבוע את זמן ההפעלה של קובץ השירות עבור בקשת ניווט ספציפית מהדלתא בין entry.responseStart ל-entry.workerStart באופן הבא:

// Catch errors some browsers throw when using the new `type` option.
// https://bugs.webkit.org/show_bug.cgi?id=209216
try {
  // 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});
} catch (e) {
  // Do nothing if the browser doesn't support this API.
}

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

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 של תזמון המשאב וממשק ה-API של תזמון הניווט.

// Catch errors some browsers throw when using the new `type` option.
// https://bugs.webkit.org/show_bug.cgi?id=209216
try {
  // 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});
} catch (e) {
  // Do nothing if the browser doesn't support this API.
}