איך להעריך את ביצועי הטעינה בשדה באמצעות 'תזמון ניווט' ו'תזמון משאבים'

מידע על העקרונות הבסיסיים של השימוש בממשקי ה-API של Navigation ו-Resource Timing להערכת ביצועי הטעינה בשטח.

תאריך פרסום: 8 באוקטובר 2021

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

בדיקה סינתטית היא לא גרועה מטבעה, אבל היא לא מייצגת את מהירות הטעינה של האתר אצל משתמשים אמיתיים. לשם כך נדרשים נתוני שדה, שאפשר לאסוף מ-Navigation Timing API ומ-Resource Timing API.

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

Navigation Timing ו-Resource Timing הם שני ממשקי API דומים עם חפיפה משמעותית, שמודדים שני דברים נפרדים:

  • המדד תזמון ניווט מודד את מהירות הבקשות למסמכי HTML (כלומר, בקשות ניווט).
  • תזמון משאבים: המדד הזה מודד את מהירות הבקשות למשאבים שמוגדרים למסמך, כמו CSS, ‏ JavaScript, תמונות וסוגים אחרים של משאבים.

ממשקי ה-API האלה חושפים את הנתונים שלהם במאגר נתוני ביצועים, שאפשר לגשת אליו בדפדפן באמצעות JavaScript. יש כמה דרכים לשליחת שאילתות למאגר ביצועים, אבל דרך נפוצה היא באמצעות performance.getEntriesByType:

// Get Navigation Timing entries:
performance.getEntriesByType('navigation');

// Get Resource Timing entries:
performance.getEntriesByType('resource');

הפונקציה performance.getEntriesByType מקבלת מחרוזת שמתארת את סוג הרשומות שרוצים לאחזר מהמאגר של רשומות הביצועים. 'navigation' ו-'resource' מאחזרים את זמני האירועים של ממשקי ה-API Navigation Timing ו-Resource Timing, בהתאמה.

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

משך החיים והתזמון של בקשת רשת

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

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

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

חיפוש DNS

כשמשתמש עובר לכתובת URL, מתבצעת שאילתה במערכת שמות הדומיינים (DNS) כדי לתרגם דומיין לכתובת IP. התהליך הזה עשוי להימשך זמן רב, אפילו משך זמן שכדאי למדוד בשטח. במדדים 'תזמון הניווט' ו'תזמון המשאבים' מוצגים שני תזמונים שקשורים ל-DNS:

  • domainLookupStart הוא המועד שבו מתחיל חיפוש ה-DNS.
  • domainLookupEnd הוא הזמן שבו חיפוש ה-DNS מסתיים.

כדי לחשב את משך הזמן הכולל של חיפוש ה-DNS, מחסירים את מדד ההתחלה ממדד הסיום:

// Measuring DNS lookup time
const [pageNav] = performance.getEntriesByType('navigation');
const totalLookupTime = pageNav.domainLookupEnd - pageNav.domainLookupStart;

משא ומתן על חיבור

גורם נוסף שמשפיע על ביצועי הטעינה הוא משא ומתן על חיבור, שהוא זמן האחזור שנוצר כשמתחברים לשרת אינטרנט. אם מעורב HTTPS, התהליך הזה יכלול גם את זמן ניהול המשא ומתן באמצעות TLS. שלב החיבור מורכב משלושה תזמונים:

  • connectStart הוא האירוע שבו הדפדפן מתחיל לפתוח חיבור לשרת אינטרנט.
  • secureConnectionStart מסמן מתי הלקוח מתחיל במשא ומתן ב-TLS.
  • connectEnd הוא הזמן שבו החיבור לשרת האינטרנט הוקם.

מדידה של זמן החיבור הכולל דומה למדידת זמן החיפוש הכולל של DNS: מחסירים את תזמון ההתחלה מתזמון הסיום. עם זאת, יש נכס secureConnectionStart נוסף שעשוי להיות 0 אם לא משתמשים ב-HTTPS, או אם החיבור קבוע. אם רוצים למדוד את זמן המשא ומתן ב-TLS, חשוב לזכור את הנקודות הבאות:

// Quantifying total connection time
const [pageNav] = performance.getEntriesByType('navigation');
const connectionTime = pageNav.connectEnd - pageNav.connectStart;
let tlsTime = 0; // <-- Assume 0 to start with

// Was there TLS negotiation?
if (pageNav.secureConnectionStart > 0) {
  // Awesome! Calculate it!
  tlsTime = pageNav.connectEnd - pageNav.secureConnectionStart;
}

בסיום חיפוש ה-DNS וניהול המשא ומתן על החיבור, נכנסים לתמונה הזמנים שקשורים לאחזור מסמכים ולמשאבים התלויים בהם.

בקשות ותגובות

יש שני סוגים של גורמים שמשפיעים על ביצועי הטעינה:

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

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

  • fetchStart מסמנת את המועד שבו הדפדפן מתחיל לאחזר משאב (זמן אחזור משאבים) או מסמך עבור בקשת ניווט (זמן ניווט). השלב הזה מתרחש לפני הבקשה בפועל, והוא השלב שבו הדפדפן בודק מטמון (לדוגמה, HTTP ומכונות Cache).
  • workerStart מסמן מתי בקשה מתחילה להיות מטופלת במטפל באירועים של fetch של Service Worker. הערך יהיה 0 אם אף קובץ שירות לא שולט בדף הנוכחי.
  • requestStart הוא המועד שבו הדפדפן שולח את הבקשה.
  • responseStart הוא הבייט הראשון של התשובה.
  • responseEnd הוא המועד שבו מגיע הבייט האחרון בתגובה.

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

// Cache seek plus response time of the current document
const [pageNav] = performance.getEntriesByType('navigation');
const fetchTime = pageNav.responseEnd - pageNav.fetchStart;

// Service worker time plus response time
let workerTime = 0;

if (pageNav.workerStart > 0) {
  workerTime = pageNav.responseEnd - pageNav.workerStart;
}

אפשר גם למדוד היבטים אחרים של זמן האחזור של בקשות ותשובות:

const [pageNav] = performance.getEntriesByType('navigation');

// Request time only (excluding redirects, DNS, and connection/TLS time)
const requestTime = pageNav.responseStart - pageNav.requestStart;

// Response time only (download)
const responseTime = pageNav.responseEnd - pageNav.responseStart;

// Request + response time
const requestResponseTime = pageNav.responseEnd - pageNav.requestStart;

מדידות נוספות שאפשר לבצע

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

  • הפניות אוטומטיות לדפים: הפניות אוטומטיות הן מקור מוזנח לעיכוב נוסף, במיוחד שרשרות של הפניות אוטומטיות. זמן האחזור מתווסף במספר דרכים, כגון צעדים מסוג HTTP-ל-HTTP וכן הפניות 301/שלא נשמרו במטמון. התזמונים redirectStart, redirectEnd ו-redirectCount עוזרים בהערכת זמן האחזור של הפניה אוטומטית.
  • פריקה של מסמך: בדפים שמריצים קוד במתבצע אירוע unload, הדפדפן צריך להריץ את הקוד הזה לפני שהוא יכול לנווט לדף הבא. unloadEventStart ו-unloadEventEnd מודדים את טעינת המסמך.
  • עיבוד מסמכים: ייתכן שלזמן עיבוד המסמכים לא תהיה השפעה, אלא אם האתר שלכם שולח מטענים ייעודיים (payloads) גדולים מאוד של HTML. אם זה המצב שלכם, יכול להיות שתמצאו עניין בתזמונים domInteractive, domContentLoadedEventStart, domContentLoadedEventEnd ו-domComplete.

איך מקבלים תזמונים בקוד

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

הגישה המומלצת לאיסוף רשומות ממאגר הנתונים הזמני של הביצועים היא להשתמש ב-PerformanceObserver. PerformanceObserver מזהה רשומות של ביצועים ומספק אותן כשהן מתווספות למאגר הנתונים הזמני:

// Create the performance observer:
const perfObserver = new PerformanceObserver((observedEntries) => {
  // Get all resource entries collected so far:
  const entries = observedEntries.getEntries();

  // Iterate over entries:
  for (let i = 0; i < entries.length; i++) {
    // Do the work!
  }
});

// Run the observer for Navigation Timing entries:
perfObserver.observe({
  type: 'navigation',
  buffered: true
});

// Run the observer for Resource Timing entries:
perfObserver.observe({
  type: 'resource',
  buffered: true
});

השיטה הזו של איסוף תזמונים עשויה להיראות מוזרה בהשוואה לגישה ישירה למאגר הנתונים הזמני של הביצועים, אבל עדיף לקשר את ה-thread הראשי לעבודה שלא משרתת מטרה קריטית ולא משרתת את המשתמשים.

איך להתקשר הביתה

אחרי שאתם אוספים את כל הזמנים הנדרשים, אתם יכולים לשלוח אותם לנקודת קצה לצורך ניתוח נוסף. אפשר לעשות זאת בשתי דרכים: באמצעות navigator.sendBeacon או באמצעות fetch עם ההגדרה של האפשרות keepalive. בשתי השיטות תישלח בקשה לנקודת קצה שהוגדרה באופן לא חוסם, והבקשה תתווסף לתור כך שתישאר בתוקף גם אחרי סיום הסשן הנוכחי בדף, אם יהיה צורך:

// Check for navigator.sendBeacon support:
if ('sendBeacon' in navigator) {
  // Caution: If you have lots of performance entries, don't
  // do this. This is an example for illustrative purposes.
  const data = JSON.stringify(performance.getEntries());

  // Send the data!
  navigator.sendBeacon('/analytics', data);
}

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

סיכום

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

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

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

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