בסדנת התכנות הזו תלמדו איך להשתמש ב-Google Analytics וב-User Timings API כדי למדוד את הביצועים של האתר או האפליקציה בפועל ולבצע אופטימיזציה כדי לשפר את חוויית המשתמש.
כלים כמו WebPagetest.org הם התחלה מצוינת לאופטימיזציה של הביצועים, אבל המבחן האמיתי של ביצועי האתר הוא תמיד נתונים אמיתיים ממשתמשים בפועל.
אם יש לכם אתר, סביר להניח שאתם כבר משתמשים ב-Google Analytics כדי למדוד את תנועת הגולשים, וגם נתונים כמו שימוש במכשירים ובדפדפנים. בעזרת קוד נוסף קצר אפשר להוסיף מדדי ביצועים.
מה תלמדו
- איך למדוד ביעילות ובדייקנות מדדי ביצועים באמצעות User Timings API
- איך שולחים את הנתונים האלה ל-Google Analytics כדי שאפשר יהיה לשלב אותם בדוחות
מה נדרש
- דפדפן עם Developer Console
- Web Server for Chrome או להשתמש בשרת אינטרנט משלכם
- קוד לדוגמה
- כלי לעריכת טקסט
- (אופציונלי) חשבון Google Analytics
איך תשתמשו במדריך הזה?
מה מידת שביעות הרצון שלך מחוויית בניית אתרים או אפליקציות?
אפשר להוריד את כל הקוד לדוגמה למחשב...
…או משכפלים את המאגר ב-GitHub משורת הפקודה.
git clone https://github.com/googlecodelabs/performance-analytics.git
הקוד לדוגמה מחולק לספריות משנה שתואמות לכל אחד מהשלבים הממוספרים ב-Code Lab הזה. אפשר להשתמש בזה כדי לדלג בקלות בין חלקי הקוד או כדי לוודא שההטמעה נכונה.
אם יש לכם גישה לתוכנה להשוואה בין קבצים, אתם יכולים להשתמש בה כדי לראות בדיוק מה השתנה משלב לשלב.
בשיעור ה-Lab הזה, תעבדו עם קובץ HTML יחיד שמעלה את הנכסים הבאים:
- גופנים לאינטרנט
- גיליונות סגנונות
- תמונות
- JavaScript
ותכתבו קוד חדש שמודד את מדדי הביצועים המרכזיים לכל אחד מסוגי הנכסים האלה.
שיקולים לגבי ביצועי נכסים
אם קראתם פעם משהו על אופטימיזציה של ביצועים, אתם כנראה כבר יודעים שלכל אחד מסוגי הנכסים האלה יש מאפיינים ייחודיים משלו, והוא יכול להשפיע על הביצועים הכוללים בדרכים שונות.
CSS
לדוגמה, גיליונות סגנונות חוסמים את העיבוד של כל הרכיבים ב-DOM שמופיעים אחריהם, מה שאומר שהדפדפן צריך לשלוח בקשה לגיליון הסגנונות, להוריד אותו ולנתח אותו לפני שהוא יכול לעבד את התוכן ב-DOM שמופיע אחריו. לכן, בדרך כלל הכי טוב למקם את גיליונות הסגנונות בתג <head> של המסמך. בגלל האופי החוסם של CSS, מומלץ גם למקם רק את ה-CSS הקריטי בתג <head> ולטעון את ה-CSS הלא קריטי באופן אסינכרוני לאחר מכן.
JavaScript
לעומת זאת, JavaScript לא חוסם את הרינדור, אבל הוא חוסם את הניתוח והבנייה של ה-DOM. הדבר נחוץ כי JavaScript יכול לשנות את ה-DOM, כלומר בכל פעם שהדפדפן רואה תג <script> (לא כולל סקריפטים אסינכרוניים), הוא חייב להריץ את הקוד לפני שהוא ממשיך לתג הבא. אם תג ה-<script> מפנה לקובץ JavaScript חיצוני, המערכת צריכה להוריד ולהפעיל את הקוד לפני שתמשיך.
לכן, מומלץ בדרך כלל לטעון את קוד ה-JavaScript ממש לפני תג הסגירה </body>, כדי שרוב ה-DOM יהיה זמין כמה שיותר מהר.
גופנים לאינטרנט
אם טוענים גופני אינטרנט, אפשר גם לחסום את העיבוד של המסמך עד שהגופן יהיה זמין לשימוש. במקרה כזה, חשוב מאוד להבין כמה זמן זה באמת לוקח למשתמשים שלכם. יכול להיות שגופן אינטרנט ייטען אצלכם במהירות, אבל אצל רוב האנשים שמבקרים באתר הוא ייטען לאט מאוד. לכן חשוב כל כך למדוד את הביצועים ולקבל החלטות על סמך נתונים אמיתיים.
תמונות
לבסוף, תמונות יכולות להחיות את האתר, אבל הן גם יכולות לקחת הכי הרבה זמן לטעינה. כדי להבין איך לבצע אופטימיזציה, חשוב להבין את המשמעות של הנתונים האלה ולזהות קשרים בין דפוסי השימוש לבין זמני טעינת הדפים.
השלב הראשון ב-Code Lab הזה הוא לראות איך נראה דף ההדגמה לפני שמוסיפים אליו קוד למדידת ביצועים.
כדי לראות את ההדגמה, יוצרים תיקייה חדשה ומוסיפים לתוכה קובץ בשם index.html. לאחר מכן מעתיקים את הקוד שבהמשך ומדביקים אותו בקובץ index.html.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Performance Analytics Demo</title>
<!-- Start fonts -->
<link href="https://fonts.googleapis.com/css?family=Roboto:400,700,400italic" rel="stylesheet">
<!-- End fonts -->
<!-- Start CSS -->
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet">
<style>
body { font-family: Roboto, sans-serif; margin: 1em; }
img { float: left; height: auto; width: 33.33%; }
.gallery { overflow: hidden; }
</style>
<!-- End CSS -->
</head>
<body>
<div class="container">
<!-- Start images -->
<div class="gallery">
<img src="http://lorempixel.com/380/200/animals/1/">
<img src="http://lorempixel.com/380/200/animals/2/">
<img src="http://lorempixel.com/380/200/animals/3/">
</div>
<!-- End images -->
<h1>Performance Analytics Demo</h1>
<p>Real performance data from real users.</p>
</div>
<!-- Start JavaScript -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
<!-- End JavaScript -->
</body>
</html>לאחר מכן, פותחים את Web Server for Chrome ומתחילים שרת מקומי בספרייה שיצרתם. חשוב לוודא שתיבת הסימון 'הצגה אוטומטית של index.html' מסומנת.

עכשיו אמורה להיות לכם אפשרות לעבור אל http://127.0.0.1:8887/ בדפדפן ולראות את קובץ ההדגמה. הוא אמור להיראות כך:

אחרי שמריצים את דף ההדגמה, כדאי להסתכל על הקוד ולראות את כל סוגי הנכסים השונים שנטענים. בשלבים הבאים תוסיפו קוד למדידה של טעינת הנכסים האלה, כדי לדעת מתי המשתמש יכול לקיים איתם אינטראקציה.
כמו שצוין קודם בקטע בנושא שיקולים לגבי ביצועי נכסים, CSS חוסם את העיבוד של רכיבי DOM ואת ההפעלה של סקריפטים שמופיעים אחריו ב-DOM.
קובץ ההדגמה שיצרתם מכיל את ה-CSS הבא, שמתייחס ל-Bootstrap ולכמה סגנונות מוטבעים.
<!-- Start CSS -->
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet">
<style>
body { font-family: Roboto, sans-serif; margin: 1em; }
img { float: left; height: auto; width: 33.33%; }
.gallery { overflow: hidden; }
</style>
<!-- End CSS -->מכיוון ש-CSS חוסם גם את העיבוד של רכיבי DOM וגם את ההפעלה של סקריפטים, אפשר לקבוע מתי ה-CSS סיים לחסום על ידי הוספת תג <script> מיד אחרי ה-CSS שמאחסן את השעה הנוכחית.
אפשר לעשות את זה על ידי יצירת משתנה והקצאת הערך new Date() אליו, אבל הודות ל-User Timings API, יש דרך הרבה יותר קלה: השיטה performance.mark.
כדי לסמן מתי שירות ה-CSS סיים לחסום את הרינדור ואת הפעלת הסקריפט, מוסיפים את שורת הקוד הבאה ממש לפני ההערה הסוגרת <!-- End CSS -->.
<script>performance.mark('css:unblock');</script>השיטה performance.mark יוצרת חותמת זמן ברזולוציה גבוהה בנקודת הזמן המדויקת הזו, ומשייכת אותה לשם שהועבר לשיטה. בדוגמה הזו, שם הסימון הוא css:unblock.
השיטה performance.mark משמשת יחד עם השיטה performance.measure, שמשמשת לחישוב ההבדל בזמן בין שתי נקודות ציון (בנוסף לנקודות הציון שאתם יוצרים, אתם יכולים להשתמש גם בנקודות הציון שהדפדפן יוצר באופן אוטומטי עבור הנקודות השונות ב-Navigation Timing API).
פונקציית השירות הבאה מודדת ומחזירה את משך הזמן בין סימן שהוספתם לבין הסימן responseEnd שנוצר על ידי Navigation Timing API.
function measureDuration(mark, opt_reference) {
var reference = opt_reference || 'responseEnd';
var name = reference + ':' + mark;
// Clears any existing measurements with the same name.
performance.clearMeasures(name);
// Creates a new measurement from the reference point to the specified mark.
// If more than one mark with this name exists, the most recent one is used.
performance.measure(name, reference, mark);
// Gets the value of the measurement just created.
var measure = performance.getEntriesByName(name)[0];
// Returns the measure duration.
return measure.duration;
}כדי להתחיל להשתמש בפונקציית השירות הזו, יוצרים קובץ חדש בשם perf-analytics.js (באותה ספרייה שבה נמצא הקובץ index.html), ומעתיקים את הקוד שלמעלה ומדביקים אותו בקובץ.
עכשיו, אחרי שהגדרתם את הפונקציה הזו, אתם יכולים לקרוא לה ולהעביר את שם התג 'css:unblock'. כדי לא להפריע לטעינה של משאבים אחרים, כדאי לדחות את ההפעלה של המדידות האלה עד אחרי שמופעל אירוע הטעינה של החלון.
אחרי שכותבים פונקציה לקריאה לקוד הזה, קובץ perf-analytics.js אמור להיראות כך:
window.onload = function() {
measureCssUnblockTime();
};
/**
* Calculates the time duration between the responseEnd timing event and when
* the CSS stops blocking rendering, then logs that value to the console.
*/
function measureCssUnblockTime() {
console.log('CSS', 'unblock', measureDuration('css:unblock'));
}
/**
* Accepts a mark name and an optional reference point in the navigation timing
* API and returns the time duration between the reference point and the last
* mark (chronologically).
* @param {string} mark The mark name.
* @param {string=} opt_reference An optional reference point from the
* navigation timing API. Defaults to 'responseEnd'.
* @return {number} The time duration
*/
function measureDuration(mark, opt_reference) {
var reference = opt_reference || 'responseEnd';
var name = reference + ':' + mark;
// Clears any existing measurements with the same name.
performance.clearMeasures(name);
// Creates a new measurement from the reference point to the specified mark.
// If more than one mark with this name exists, the most recent one is used.
performance.measure(name, reference, mark);
// Gets the value of the measurement just created.
var measure = performance.getEntriesByName(name)[0];
// Returns the measure duration.
return measure.duration;
}לבסוף, צריך לטעון את הסקריפט של perf-analytics.js מ-index.html. כדי לעשות זאת, מוסיפים את תג הסקריפט הבא למסמך הראשי. חשוב להוסיף אותו אחרון כדי שלא יפריע לטעינה של משאבים אחרים.
<!-- Start performance analytics -->
<script async src="perf-analytics.js"></script>
<!-- End performance analytics -->אחרי שתשלימו את השלב הזה, הקוד שלכם צריך להיות זהה לקוד שבספרייה 01-css במאגר של שיעור ה-Lab.
אם טוענים את הדף בדפדפן ופותחים את מסוף הפיתוח, אמור להופיע פלט שדומה לזה:

גופני אינטרנט נטענים בדרך כלל דרך גיליון סגנונות חיצוני, כמו שרואים בקובץ ההדגמה הראשוני:
<!-- Start fonts -->
<link href="https://fonts.googleapis.com/css?family=Roboto:400,700,400italic" rel="stylesheet">
<!-- End fonts -->מכיוון שזהו תג <link> לקובץ CSS, יכול להיות שנדמה שקביעת המועד שבו הגופנים נטענים ומוכנים לשימוש היא פשוטה כמו הוספת סימן בתוך תג <script> מיד אחרי התג <link>, בדיוק כמו בשלב 1.
לצערי, זה לא כל כך פשוט.
גיליונות סגנונות חוסמים את ההפעלה של JavaScript כי התוכן של גיליונות הסגנונות משמש לבניית CSSOM. מכיוון שיכול להיות שסקריפט ה-JavaScript שנמצא בטעינה יצטרך לגשת ל-CSSOM, צריך לדחות את ההפעלה עד ש-CSSOM ייבנה במלואו.
הבעיה היא שהדפדפן יכול ליצור את ה-CSSOM בלי להוריד את הגופן בפועל. כלומר, אם מוסיפים סימון באמצעות תג סקריפט מוטבע ל-DOM מיד אחרי תג הגיליון הראשי של הגופן <link>, סביר להניח שהסימון יקרה לפני שהגופן ייטען במלואו.
עד שאירועי טעינת גופנים יהיו זמינים בדפדפנים, צריך להשתמש ב-JavaScript כדי לקבוע מתי גופן באמת פעיל ומוכן לשימוש בדף. למזלנו, טעינת גופנים באמצעות JavaScript גם משפרת את הביצועים, כי היא לא דורשת בקשת חסימה נוספת לקובץ CSS.
אפשר לטעון את רוב גופני האינטרנט (כולל גופנים של Google, Typekit ו-font.com) באמצעות הסקריפט webfont.js, שפותח בשיתוף של Google ו-Typekit.
כדי לעדכן את המסמך הראשי כך שישתמש ב-webfont.js לטעינת הגופנים (במקום בתג <link>), מחליפים את קטע הגופנים בקוד בקטע הבא:
<!-- Start fonts -->
<script>
window.WebFontConfig = {
google: {families: ['Roboto:400,700,400italic']},
timeout: 10000,
active: function() {
performance.mark('fonts:active');
}
};
</script>
<script async src="https://ajax.googleapis.com/ajax/libs/webfont/1/webfont.js"></script>
<!-- End fonts -->יש שני דברים חשובים שכדאי לשים לב אליהם בקוד שלמעלה:
- היא יוצרת סימן fonts:active בקריאה החוזרת הפעילה, כדי שתוכלו למדוד מאוחר יותר כמה זמן לקח לגופנים להיטען.
- התג
<script>שמעלה את webfonts.js מכיל את המאפייןasync, ולכן הוא לא יחסום את הניתוח או את העיבוד של שאר המסמך (מה שלא נכון לגבי תגי<link>).
הקודים שלמעלה יוצרים את הסימן fonts:active, אבל מדידת הסימן הזה ורישום שלו במסוף לא פשוטים כמו מדידת הסימן css:unblock. הסיבה לכך היא שעכשיו טעינת הגופן מתבצעת באופן אסינכרוני, כך שאם תנסו למדוד את הסימן fonts:active ב-handler של window.onload (כמו שעשיתם עם css:unblock), סביר מאוד שהגופן עדיין לא ייטען.
כדי לפתור את הבעיה, אפשר ליצור הבטחה שמתקיימת אחרי שהגופן נטען. הפונקציה הבאה עושה את זה בשבילכם. מעתיקים אותו ומדביקים אותו בקובץ perf-analytics.js:
/**
* Creates a promise that is resolved once the web fonts are fully load or
* is reject if the fonts fail to load. The resolved callback calculates the
* time duration between the responseEnd timing event and when the web fonts
* are downloaded and active. If an error occurs loading the font, this fact
* is logged to the console.
*/
function measureWebfontPerfAndFailures() {
new Promise(function(resolve, reject) {
// The classes `wf-active` or `wf-inactive` are added to the <html>
// element once the fonts are loaded (or error).
var loaded = /wf-(in)?active/.exec(document.documentElement.className);
var success = loaded && !loaded[1]; // No "in" in the capture group.
// If the fonts are already done loading, resolve immediately.
// Otherwise resolve/reject in the active/inactive callbacks, respectively.
if (loaded) {
success ? resolve() : reject();
}
else {
var originalAciveCallback = WebFontConfig.active;
WebFontConfig.inactive = reject;
WebFontConfig.active = function() {
originalAciveCallback();
resolve();
};
// In case the webfont.js script fails to load, always reject the
// promise after the timeout amount.
setTimeout(reject, WebFontConfig.timeout);
}
})
.then(function() {
console.log('Fonts', 'active', measureDuration('fonts:active'));
})
.catch(function() {
console.error('Error loading web fonts')
});
}צריך גם לעדכן את ה-handler של window.onload כדי לקרוא לפונקציה החדשה הזו
window.onload = function() {
measureCssUnblockTime();
measureWebfontPerfAndFailures();
};אחרי שתשלימו את השלב הזה, הקוד שלכם צריך להיות זהה לקוד שבספרייה 02-fonts במאגר של שיעור ה-Lab.
אם טוענים את הדף בדפדפן ופותחים את מסוף הפיתוח, אמור להופיע פלט שדומה לזה:

לדעת מתי תמונה גלויה זה לא פשוט כמו שזה נראה. מהשלבים הקודמים אתם יודעים שגיליונות סגנונות ותגי <script> סינכרוניים יכולים לחסום את העיבוד, הניתוח והרצת הסקריפט. מה שאתם אולי לא יודעים הוא שאף אחת מהן לא חוסמת את סורק הטעינה מראש של הדפדפן.
סורק טעינה מראש הוא תכונה שמוטמעת בכל הדפדפנים המודרניים כאחת מתוך הרבה דרכים לשיפור הביצועים, גם באתרים שלא מיועדים לשיפור הביצועים ומכילים הרבה נכסים שחוסמים את הטעינה. הרעיון הוא שגם אם נכסים מסוימים חוסמים ניתוח, עיבוד או הפעלה של סקריפטים, הם לא צריכים לחסום הורדות. לכן, הדפדפן סורק את קובץ ה-HTML לפני שהוא מתחיל לבנות את ה-DOM, ומחפש נכסים שהוא יכול להתחיל להוריד באופן מיידי.
בנוגע לתמונות, המשמעות היא שיש סיכוי סביר שהתמונות כבר יורדו עד שהן יתווספו ל-DOM. המשמעות היא גם שהנקודה שבה תמונה מורדת היא לא בהכרח מדד טוב לביצועים. מדד הביצועים שחשוב להתייחס אליו הוא הנקודה שבה התמונה גלויה למשתמש.
כשמורידים תמונה לפני שמוסיפים אותה ל-DOM, הנקודה שבה היא הופכת לגלויה היא הנקודה שבה היא נמצאת ב-DOM. לעומת זאת, אם תמונה לא מורדת לפני שהיא מתווספת ל-DOM, הנקודה שבה היא הופכת לגלויות היא הנקודה שבה מופעל ה-handler שלה onload.
לכן, כדי לדעת מתי תמונה גלויה, צריך לטפל בשני המקרים.
כדי לעשות את זה, מוסיפים סימנים ל-handler של onload של כל תמונה, וגם לתג <script> מוטבע מיד אחרי התמונה האחרונה ב-DOM. הרעיון הוא שהסימון שמופיע אחרון הוא הסימון שמייצג את הרגע שבו כל התמונות גלויות.
כדי להוסיף סימנים גם לזמן הטעינה של התמונות וגם לזמן העיבוד שלהן, מעדכנים את קוד התמונות ב-index.html באופן הבא:
<!-- Start images -->
<div class="gallery">
<img onload="performance.mark('img:visible')" src="http://lorempixel.com/380/200/animals/1/">
<img onload="performance.mark('img:visible')" src="http://lorempixel.com/380/200/animals/2/">
<img onload="performance.mark('img:visible')" src="http://lorempixel.com/380/200/animals/3/">
</div>
<script>performance.mark('img:visible')</script>
<!-- End images -->מכיוון שהשיטה performance.measure לשם סימן מסוים תמיד תשתמש בסימן האחרון (אם היא מוצאת כמה סימנים עם אותו שם), אפשר להשתמש בפונקציית השירות measureDuration בקובץ perf-analytics.js בלי לבצע שינויים נוספים:
/**
* Calculates the time duration between the responseEnd timing event and when
* all images are loaded and visible on the page, then logs that value to the
* console.
*/
function measureImagesVisibleTime() {
console.log('Images', 'visible', measureDuration('img:visible'));
}מוסיפים את הפונקציה שלמעלה לקובץ perf-analytics.js, ואז מעדכנים את ה-handler של window.onload כדי לקרוא לה:
window.onload = function() {
measureCssBlockTime();
measureWebfontPerfAndFailures();
measureImagesVisibleTime();
};אחרי שתשלימו את השלב הזה, הקוד שלכם צריך להיות זהה לקוד שבספרייה 03-images במאגר של שיעור ה-Lab.
אם טוענים את הדף בדפדפן ופותחים את מסוף הפיתוח, אמור להופיע פלט שדומה לזה:

מכיוון שתגי <script> ללא מאפיין async חוסמים את ניתוח ה-DOM עד שהם מורדים ומופעלים, אפשר לקבוע את הנקודה שבה כל הסקריפטים סיימו את ההפעלה על ידי הוספת סימן בתג סקריפט מוטבע מיד אחרי תג <script> הסינכרוני האחרון ב-DOM.
הערה: שימוש ב-onload handlers לא יעבוד במקרה הזה, כי הדפדפן צריך להריץ את הסקריפט אחרי שהוא נטען, וזה לוקח זמן. סקריפט שנטען במהירות אבל ההרצה שלו איטית יכול להיות גרוע בדיוק כמו סקריפט שנטען לאט.
כדי לעקוב אחרי הטעינה וההפעלה של כל קוד ה-JavaScript במסמך הראשי, מעדכנים את קטע ה-JavaScript ב-index.html באמצעות הקוד הבא:
<!-- Start JavaScript -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
<script>performance.mark('js:execute');</script>
<!-- End JavaScript -->הפעולה הזו תוסיף סימן בשם js:execute מיד אחרי שהסקריפטים של jQuery והתוספים של Bootstrap יסיימו את ההורדה וההפעלה.
כדי למדוד כמה זמן זה לוקח, מוסיפים את הפונקציה הבאה ל-perf-analytics.js:
/**
* Calculates the time duration between the responseEnd timing event and when
* all synchronous JavaScript files have been downloaded and executed, then
* logs that value to the console.
*/
function measureJavaSciptExecutionTime() {
console.log('JavaScript', 'execute', measureDuration('js:execute'));
}ואז מפעילים אותו מה-handler של window.onload:
window.onload = function() {
measureCssBlockTime();
measureWebfontPerfAndFailures();
measureImagesVisibleTime();
measureJavaSciptExecutionTime();
};אחרי שתשלימו את השלב הזה, הקוד שלכם צריך להיות זהה לקוד שבספרייה 04-javascript במאגר של שיעור ה-Lab.
אם טוענים את הדף בדפדפן ופותחים את מסוף הפיתוח, אמור להופיע פלט שדומה לזה:

לא כל הדפדפנים תומכים בהבטחות של JavaScript או בממשקי ה-API של User Timing, ואם מריצים את הקוד שכתבתם עד עכשיו בדפדפן שלא תומך באחת מהתכונות האלה, יופיעו שגיאות.
כדי לפתור את הבעיה הזו, אפשר להשתמש בזיהוי תכונות. מוסיפים את קטע הקוד הבא מיד לפני הקטע fonts. שורה של JavaScript הזו מזהה תמיכה בשיטה performance.mark, ולכן צריך להוסיף אותה לדף לפני שמשתמשים בשיטה הזו:
<!-- Start feature detects -->
<script>window.__perf = window.performance && performance.mark;</script>
<!-- End feature detects -->לאחר מכן, בכל מקום ב-index.html שבו אתם מתקשרים אל performance.mark, מוסיפים לפניו את זיהוי התכונה. הנה דוגמה לעדכון הקוד בבלוק התמונה, אבל חשוב לעדכן גם את שאר הקטעים.
<!-- Start images -->
<div class="gallery">
<img onload="__perf && performance.mark('img:visible')" src="http://lorempixel.com/380/200/animals/1/">
<img onload="__perf && performance.mark('img:visible')" src="http://lorempixel.com/380/200/animals/2/">
<img onload="__perf && performance.mark('img:visible')" src="http://lorempixel.com/380/200/animals/3/">
</div>
<script>__perf && performance.mark('img:visible')</script>
<!-- End images -->עכשיו, אחרי שהמשתנה __perf מוגדר ב-window, אפשר להשתמש בו גם ב-perf-analytics.js כדי לוודא שלא מופעלת שיטה שלא נתמכת בדפדפן הנוכחי.
צריך לעדכן את הפונקציה measureDuration כדי להוסיף את התנאי הבא:
function measureDuration(mark, opt_reference) {
if (window.__perf) {
// ...
}
}לבסוף, מכיוון שלא כל הדפדפנים תומכים בהבטחות של JavaScript, צריך גם לעטוף את הפונקציה measureWebfontPerfAndFailures בתנאי:
function measureWebfontPerfAndFailures() {
if (window.Promise) {
// ...
}
}עכשיו אמורה להיות לכם אפשרות להריץ את הקוד בכל דפדפן בלי בעיות.
אחרי שתשלימו את השלב הזה, הקוד שלכם צריך להיות זהה לקוד שבספרייה 05-feature-detects במאגר של שיעור ה-Lab.
השלב האחרון ב-codelab הזה הוא לקחת את הנתונים שנרשמים במסוף ולשלוח אותם ל-Google Analytics במקום זאת. כדי לשלוח נתונים ל-Google Analytics, צריך להוסיף לדף את הספרייה analytics.js ואת קטע קוד המעקב שמוגדר כברירת מחדל.
מוסיפים את הקוד הבא אל index.html אחרי בלוק ה-JavaScript הראשי, אבל לפני טעינת הסקריפט perf-analytics.js:
<!-- Start analytics tracking snippet -->
<script>
window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
ga('create', 'UA-XXXXX-Y', 'auto');
ga('send', 'pageview');
</script>
<script async src="https://www.google-analytics.com/analytics_debug.js"></script>
<!-- End analytics tracking snippet -->אם כבר הוספתם את Google Analytics לאתר, אתם יודעים שצריך להחליף את placeholder [UA-XXXXX-Y] במזהה לצורכי מעקב שקיבלתם כשיוצרים נכס חדש ב-Google Analytics.
קטע הקוד למעקב analytics.js מבצע ארבע פעולות עיקריות:
- יוצר רכיב
<script>אסינכרוני שמוריד את ספריית analytics.js של JavaScript. - יוצרת פונקציה גלובלית
ga()(שנקראת תור הפקודות ga()) שמאפשרת לתזמן פקודות להפעלה אחרי שהספרייה analytics.js נטענת ומוכנה לשימוש. - מוסיף פקודה לתור הפקודות של
ga()כדי ליצור אובייקט חדש של מעקב עבור הנכס שצוין באמצעות הפרמטר UA-XXXXX-Y. - הוספת פקודה נוספת לתור הפקודות של
ga()כדי לשלוח צפייה בדף ל-Google Analytics עבור הדף הנוכחי.
הנתונים שנאספים רק מצפיות בדפים הם שימושיים, אבל הם לא מספרים את כל הסיפור. כדי לקבל תמונה טובה יותר של חוויית המשתמש באתר או באפליקציה, צריך לשלוח נתוני אינטראקציה נוספים אל Google Analytics.
מערכת Google Analytics תומכת בכמה סוגים של נתוני אינטראקציה: צפיות בדף, אירועים, אינטראקציות ברשתות חברתיות, חריגים ו (לא פחות חשוב) תזמונים של משתמשים. כדי לשלוח נתוני תזמון משתמשים ל-Google Analytics, אפשר להשתמש בחתימת הפקודה הבאה:
ga('send', 'timing', timingCategory, timingVar, timingValue);timingCategory הוא מחרוזת שמאפשרת לארגן את נתוני התזמון בקבוצה לוגית, timingVar הוא המשתנה שנמדד ו-timingValue הוא משך הזמן בפועל באלפיות השנייה.
כדי לראות איך זה עובד בפועל, אפשר לעדכן את ההצהרה console.log בפונקציה measureCssUnblockTime באופן הבא:
ga('send', 'timing', 'CSS', 'unblock', measureDuration('css:unblock'));הקוד שלמעלה יעבוד בחלק מהמקרים, אבל יש שני דברים חשובים שצריך לדעת:
- בשלב הקודם עדכנו את הפונקציה
measureDurationכך שתפעל רק אם הדפדפן תומך ב-User Timings API, מה שאומר שלפעמים היא תחזיר את הערךundefined. אין סיבה לשלוח נתונים לא מוגדרים ל-Google Analytics (במקרים מסוימים זה אפילו עלול לשבש את הדוחות), ולכן צריך לשלוח את היט התזמון הזה רק אםmeasureDurationמחזיר ערך. - אם הפונקציה
measureDurationמחזירה ערך, זהוDOMHighResTimeStamp, עם דיוק גבוה ממילי-שנייה. מכיוון שהערך שלtimingValueב-Google Analytics חייב להיות מספר שלם, צריך לעגל את הערך שמוחזר על ידיmeasureDuration.
כדי להתמודד עם הבעיות האלה, צריך לעדכן את פקודת החזרה (return) בפונקציה measureDuration כדי לעגל את הערך שמוחזר:
function measureDuration(mark, opt_reference) {
if (window.__perf) {
// ...
return Math.round(measure.duration);
}
}מעדכנים את פקודות התזמון כך שהן יפעלו רק אם קיים ערך למדד הרלוונטי. לדוגמה, הפונקציה measureCssUnblockTime צריכה להתעדכן למשהו כזה:
function measureCssUnblockTime() {
var cssUnblockTime = measureDuration('css:unblock');
if (cssUnblockTime) {
ga('send', 'timing', 'CSS', 'unblock', cssUnblockTime);
}
}תצטרכו לבצע עדכונים דומים בכל שאר פונקציות המדידה. אחרי ההשלמה, הקובץ הסופי perf-analytics.js אמור להיראות כך:
window.onload = function() {
measureCssUnblockTime();
measureWebfontPerfAndFailures();
measureImagesVisibleTime();
measureJavaSciptExecutionTime();
};
/**
* Calculates the time duration between the responseEnd timing event and when
* the CSS stops blocking rendering, then sends this measurement to Google
* Analytics via a timing hit.
*/
function measureCssUnblockTime() {
var cssUnblockTime = measureDuration('css:unblock');
if (cssUnblockTime) {
ga('send', 'timing', 'CSS', 'unblock', cssUnblockTime);
}
}
/**
* Calculates the time duration between the responseEnd timing event and when
* the web fonts are downloaded and active, then sends this measurement to
* Google Analytics via a timing hit. If an error occurs loading the font, an
* error event is sent to Google Analytics.
*/
function measureWebfontPerfAndFailures() {
if (window.Promise) {
new Promise(function(resolve, reject) {
var loaded = /wf-(in)?active/.exec(document.documentElement.className);
var success = loaded && !loaded[1]; // No "in" in the capture group.
if (loaded) {
success ? resolve() : reject();
}
else {
var originalAciveCallback = WebFontConfig.active;
WebFontConfig.inactive = reject;
WebFontConfig.active = function() {
originalAciveCallback();
resolve();
};
// In case the webfont.js script failed to load.
setTimeout(reject, WebFontConfig.timeout);
}
})
.then(function() {
var fontsActiveTime = measureDuration('fonts:active');
if (fontsActiveTime) {
ga('send', 'timing', 'Fonts', 'active', fontsActiveTime);
}
})
.catch(function() {
ga('send', 'event', 'Fonts', 'error');
});
}
}
/**
* Calculates the time duration between the responseEnd timing event and when
* all images are loaded and visible on the page, then sends this measurement
* to Google Analytics via a timing hit.
*/
function measureImagesVisibleTime() {
var imgVisibleTime = measureDuration('img:visible');
if (imgVisibleTime) {
ga('send', 'timing', 'Images', 'visible', imgVisibleTime);
}
}
/**
* Calculates the time duration between the responseEnd timing event and when
* all synchronous JavaScript files are downloaded and executed, then sends
* this measurement to Google Analytics via a timing hit.
*/
function measureJavaSciptExecutionTime() {
var jsExecuteTime = measureDuration('js:execute');
if (jsExecuteTime) {
ga('send', 'timing', 'JavaScript', 'execute', jsExecuteTime);
}
}
/**
* Accepts a mark name and an optional reference point in the navigation timing
* API and returns the time duration between the reference point and the last
* mark (chronologically). The return value is rounded to the nearest whole
* number to be compatible with Google Analytics.
* @param {string} mark The mark name.
* @param {string=} opt_reference An optional reference point from the
* navigation timing API. Defaults to 'responseEnd'.
* @return {?number} The time duration as an integer or undefined if no
* matching marks can be found.
*/
function measureDuration(mark, opt_reference) {
if (window.__perf) {
var reference = opt_reference || 'responseEnd';
var name = reference + ':' + mark;
// Clears any existing measurements with the same name.
performance.clearMeasures(name);
// Creates a new measurement from the reference point to the specified mark.
// If more than one mark with this name exists, the most recent one is used.
performance.measure(name, reference, mark);
// Gets the value of the measurement just created.
var measure = performance.getEntriesByName(name)[0];
// Returns the measure duration.
return Math.round(measure.duration);
}
}וקובץ index.html הסופי צריך להיראות כך:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Performance Analytics Demo</title>
<!-- Start navigation timing feature detect -->
<script>window.__perf = window.performance && performance.mark;</script>
<!-- End navigation timing feature detect -->
<!-- Start fonts -->
<script>
window.WebFontConfig = {
google: {families: ['Roboto:400,700,400italic']},
timeout: 10000,
active: function() {
__perf && performance.mark('fonts:active');
}
};
</script>
<script async src="https://ajax.googleapis.com/ajax/libs/webfont/1/webfont.js"></script>
<!-- End fonts -->
<!-- Start CSS -->
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet">
<style>
body { font-family: Roboto, sans-serif; margin: 1em; }
img { float: left; height: auto; width: 33.33%; }
.gallery { overflow: hidden; }
</style>
<script>__perf && performance.mark('css:unblock');</script>
<!-- End CSS -->
</head>
<body>
<div class="container">
<!-- Start images -->
<div class="gallery">
<img onload="__perf && performance.mark('img:visible')" src="http://lorempixel.com/380/200/animals/1/">
<img onload="__perf && performance.mark('img:visible')" src="http://lorempixel.com/380/200/animals/2/">
<img onload="__perf && performance.mark('img:visible')" src="http://lorempixel.com/380/200/animals/3/">
</div>
<script>__perf && performance.mark('img:visible')</script>
<!-- End images -->
<h1>Performance Analytics Demo</h1>
<p>Real performance data from real users.</p>
</div>
<!-- Start JavaScript -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
<script>__perf && performance.mark('js:execute');</script>
<!-- End JavaScript -->
<!-- Start analytics tracking snippet -->
<script>
window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
ga('create', 'UA-XXXXX-Y', 'auto');
ga('send', 'pageview');
</script>
<script async src="https://www.google-analytics.com/analytics.js"></script>
<!-- End analytics tracking snippet -->
<!-- Start performance analytics -->
<script async src="perf-analytics.js"></script>
<!-- End performance analytics -->
</body>
</html>אם תטענו את הדף הזה ותבדקו את הבקשות בחלונית הרשת, תראו משהו כזה:

הנתונים האלה שימושיים, אבל יכול להיות שיהיה קשה לעיין בהם כבקשה שמקודדת בכתובת URL. אם מסיבה כלשהי הבקשות האלה לא מופיעות, קשה מאוד לאתר את המקום שבו הכשל התרחש.
גישה טובה יותר כשמפתחים באופן מקומי היא להשתמש בגרסת ניפוי הבאגים של analytics.js, שתתעד ביומן מידע שימושי לניפוי באגים במסוף בכל פעם שמופעלת פקודה של analytics.js. אם
אם מעדכנים את כתובת ה-URL של analytics.js ב-index.html ל-analytics_debug.js ופותחים את מסוף הדפדפן, יופיעו הצהרות שנראות כך:

אחרי שהבנתם איך להטמיע מדידת ביצועים בדף ההדגמה הזה, אתם יכולים לנסות להוסיף אותה לאתר שלכם ולשלוח נתונים אמיתיים של משתמשים ל-Google Analytics.
דיווח על הנתונים שאספתם
אחרי כמה ימים של איסוף נתוני ביצועים, תוכלו ליצור דוחות על הנתונים האלה כדי לקבל תובנות מעשיות לגבי מהירות הטעינה של האתר והמשאבים שלו אצל משתמשים אמיתיים.
כדי להגיע לדוחות על תזמון משתמשים ב-Google Analytics, לוחצים על הכרטיסייה 'דיווח' בחלק העליון ובוחרים באפשרות 'התנהגות > מהירות האתר > תזמון משתמשים' בתפריט הניווט שבסרגל הצד (או פועלים לפי ההוראות לצפייה בדוח תזמון משתמשים ממרכז העזרה).
כשאתם טוענים את הדוח 'זמני תגובה של משתמשים' ב-Google Analytics, אמורות להופיע בו קטגוריות התזמון שמתאימות לנתונים ששלחתם. אפשר ללחוץ על כל אחד מהם כדי לראות תרשימים מפורטים של נתוני התזמון. התמונה הבאה היא דוגמה לזמני טעינה של גופנים באתר אמיתי באמצעות Google Fonts ב-24 השעות האחרונות.

מעולה! סיימתם בהצלחה את שיעור ה-Lab הזה. אם רוצים להעמיק, בקטע הבא יש כמה הצעות להרחבת הקוד הזה כדי לקבל תובנות נוספות.
מדדי הביצועים שמוסברים ב-Code Lab הזה חשובים מאוד למדידת מהירות הטעינה של האתר למשתמשים בפועל, אבל הם רק ההתחלה. אם אתם רוצים לנתח את הביצועים לעומק, השלב הבא הפשוט הוא לעקוב אחרי עוד מדדים.
בשיעור ה-Lab הזה תעקבו אחרי מדדים שקשורים לזמן שבו המשאבים היו זמינים למשתמש. אם רוצים, אפשר לפרק את רוב הנתונים האלה לעוד פרטים. לדוגמה, במקום למדוד רק מתי הסתיים הביצוע של JavaScript, אפשר למדוד מתי הוא התחיל להיטען, מתי הוא סיים להיטען, מתי הוא התחיל להתבצע ולבסוף מתי הוא סיים להתבצע. כל אחד מהמדדים האלה יכול לחשוף בעיה שרק אחד מהם לא יכול לחשוף.
בנוסף לניתוח מפורט יותר, כדאי גם לחשוב בצורה הוליסטית יותר על אסטרטגיית הניתוח של הביצועים הכלליים. מהן המטרות? מהי הצלחה?
כשמדובר בניתוח נתונים, בדרך כלל כדאי להתחיל עם שאלה מסוימת, ואז להבין איך להשתמש בנתונים כדי לענות על השאלה הזו.
לדוגמה, נניח שיש לכם את רשימת השאלות הבאה, ואתם רוצים להשתמש בידע שרכשתם בסדנת הקוד הזו כדי להשתמש בניתוח נתונים כדי לענות עליהן:
- האם הערכים של מדדי המעקב יורדים או עולים לאורך זמן?
- איך השימוש במטמון אופליין באמצעות Service Worker או אחסון מקומי ישפיע על הביצועים הכוללים של האתר?
- האם המשאבים נטענים בצורה אופטימלית? כלומר, האם יש פער גדול בין הזמן שבו המשאב מורד לבין הזמן שבו הוא זמין לשימוש?
- האם יש קשרים בין הביצועים לבין מדדים אחרים שאתם עוקבים אחריהם (למשל, שיעור ההרשמה, הזמן שהמשתמשים מבלים באתר, רכישות וכו')?
לבסוף, אם אתם רוצים לקבל מידע נוסף על ביצועי אתרים או על Google Analytics, הנה כמה מקורות מידע מצוינים שיעזרו לכם להתחיל:
- כלים ומידע שיעזרו לכם ליצור אתרים עם ביצועים גבוהים
https://developers.google.com/speed/ - כלים וממשקי API למפתחים לשימוש בפלטפורמת Google Analytics
https://developers.google.com/analytics/ - קורסים אונליין שמלמדים איך להשתמש במוצר Google Analytics עצמו (הקורסים מועברים על ידי אנשים שעובדים על Google Analytics עצמו).
https://analyticsacademy.withgoogle.com/