הערכת ביצועי הטעינה בשדה באמצעות תזמון ניווט ותזמון משאב

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

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

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

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

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

כמות המידע שממשקי ה-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 אם אף קובץ שירות (service worker) לא שולט בדף הנוכחי.
  • requestStart הוא המועד שבו הדפדפן שולח את הבקשה.
  • responseStart הוא הזמן שבו הבייט הראשון של התגובה מגיע.
  • responseEnd הוא הרגע שבו מגיע הבייט האחרון של התגובה.

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

// 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;

מדדים אחרים שאפשר לבצע

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

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

קבלת תזמונים בקוד האפליקציה

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

הגישה המומלצת לאיסוף רשומות ממאגר רשומות הביצועים היא להשתמש ב-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 הראשי עם עבודה שלא משרתת מטרה קריטית וגלויה למשתמשים.

טלפוניה לבית

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

// 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()));

// The endpoint to transmit the encoded data to
const endpoint = '/analytics';

// Check for fetch keepalive support
if ('keepalive' in Request.prototype) {
  fetch(endpoint, {
    method: 'POST',
    body: data,
    keepalive: true,
    headers: {
      'Content-Type': 'application/json'
    }
  });
} else if ('sendBeacon' in navigator) {
  // Use sendBeacon as a fallback
  navigator.sendBeacon(endpoint, data);
}

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

סיכום

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

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

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

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