יש חשיבות רבה ליצירת מדדים ממוקדי-משתמש שתוכלו למדוד באופן אוניברסלי בכל אתר נתון. המדדים האלה מאפשרים:
- להבין את האופן שבו משתמשים אמיתיים חווים את האינטרנט באופן כללי.
- להשוות את האתר שלך לאתר של מתחרה.
- בכלי לניתוח נתונים אפשר לעקוב אחרי נתונים שימושיים ופרקטיים בלי לכתוב קוד מותאם אישית.
מדדים אוניברסליים הם ערך בסיס טוב, אבל במקרים רבים צריך למדוד יותר מאשר רק המדדים האלה כדי ליהנות מהחוויה המלאה של האתר הספציפי.
מדדים מותאמים אישית מאפשרים לכם למדוד היבטים שקשורים לחוויית השימוש באתר, שעשויים להיות רלוונטיים רק לאתר שלכם, למשל:
- כמה זמן לוקח לאפליקציה דף יחיד (SPA) לעבור מ'דף' אחד למכשיר אחר.
- תוך כמה זמן מוצגים בדף נתונים שנשלפו ממסד נתונים עבור משתמשים שמחוברים לחשבון.
- כמה זמן לוקח לאפליקציה שמעובדת בצד השרת (SSR) להגיב במשך הזמן.
- שיעור ההיטים במטמון של משאבים שנטענו על ידי מבקרים חוזרים.
- זמן האחזור של האירוע של אירועי קליק או מקלדת במשחק.
ממשקי API למדידת מדדים מותאמים אישית
בעבר, למפתחי אתרים לא היו הרבה ממשקי API ברמה נמוכה למדידת ביצועים, וכתוצאה מכך הם נאלצו לנקוט פריצות כדי למדוד אם האתר מניב ביצועים טובים.
לדוגמה, אפשר לקבוע אם ה-thread הראשי חסום עקב משימות JavaScript ממושכות, על ידי הרצת לולאת requestAnimationFrame
וחישוב הדלתא בין כל פריים. אם הדלתא ארוכה משמעותית מקצב הפריימים במסך, אפשר לדווח על כך כמשימה ארוכה. עם זאת, לא מומלץ לבצע פריצות כאלה, כי הן משפיעות בפועל על הביצועים (למשל, על ידי התרוקנות הסוללה).
הכלל הראשון למדידת ביצועים יעילה הוא לוודא שהטכניקות שלכם למדידת הביצועים לא גורמות לבעיות בביצועים. לכן, לכל מדד מותאם אישית שאתם מודדים באתר, מומלץ להשתמש באחד מממשקי ה-API הבאים, אם אפשר.
ממשק API של Performance Observer
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 של תזמון משתמש
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
בעזרת 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 של מסגרות אנימציה ארוכות
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 לתזמון רכיבים
בעזרת המדד המהירות שבה נטען רכיב התוכן הכי גדול (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 לתזמון אירוע
המדד מהירות התגובה לאינטראקציה באתר (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 לתזמון משאבים
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});
API של תזמון ניווט
ה-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
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});