למד את היסודות של השימוש בממשקי ה-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 האלה מספקים יכולה להיות מאתגרת, אבל הם המפתח למדידת ביצועי הטעינה בשטח, כי אתם יכולים לאסוף את התזמונים האלה מהמשתמשים בזמן שהם מבקרים באתר שלכם.
משך החיים והתזמונים של בקשת רשת
איסוף וניתוח של ניווט ותזמון משאבים דומים למדי לארכיאולוגיה, בכך שאתם משחזרים את החיים הקצרים של בקשת רשת לאחר מעשה. לפעמים אפשר להיעזר בכלים למפתחים של הדפדפן כדי להמחיש מושגים ובמקרים שבהם מדובר בבקשות רשת.
לכל אורך החיים של בקשת רשת יש שלבים נפרדים, כמו חיפוש 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 או יותר, אתם מתמקדים במקום הנכון: בחוויות האיטיות ביותר.
מדריך זה לא נועד לשמש כמשאב מקיף בנושא 'ניווט' או 'תזמון משאבים', אלא רק כנקודת התחלה. ריכזנו כאן כמה מקורות מידע נוספים שעשויים להועיל לך:
- מפרטי תזמון ניווט.
- מפרט תזמון משאבים.
- ResourceTiming in Practice.
- Navigation Timing API (MDN)
- Resource Timing API (MDN)
ממשקי ה-API והנתונים שהם מספקים תספקו כלים שיעזרו לכם להבין איך משתמשים אמיתיים חווים את ביצועי הטעינה, מה שיעניק לכם יותר ביטחון באבחון בעיות של ביצועי טעינה בשטח ובטיפול בהן.