מדידה של מדדי ביצועים קריטיים באמצעות Google Analytics

בשיעור ה-Lab הזה תלמדו איך להשתמש ב-Google Analytics וב-User Timings כדי למדוד את הביצועים בפועל של האתר או האפליקציה ולבצע אופטימיזציה שלהם לשיפור חוויית המשתמשים.

כלים כמו WebPagetest.org הם התחלה נהדרת לאופטימיזציות של ביצועים, אבל הבדיקה האמיתית של ביצועי האתר תמיד תהיה נתונים אמיתיים ממשתמשים אמיתיים.

אם אתם מפעילים אתר, יש סיכוי גבוה שאתם כבר משתמשים ב-Google Analytics כדי למדוד את התנועה ואת מדדי השימוש במכשיר ובדפדפן. עם קצת קוד נוסף אפשר להוסיף למדדי מדדי ביצועים.

מה תלמדו

  • כיצד למדוד בצורה מדויקת ומדויקת את מדדי הביצועים באמצעות ה-User Timings API
  • איך שולחים את הנתונים האלה ל-Google Analytics כדי לשלב אותם בדוחות

מה תצטרך להכין

איך תשתמשו במדריך הזה?

לקרוא אותו בלבד לקרוא אותו ולהשלים את התרגילים

איך היית מדרג את החוויה שלך בבניית אתרים או אפליקציות?

מתחילים מתחילים בקיאים

אפשר להוריד את כל הקוד לדוגמה למחשב...

להורדת קובץ Zip

...או לשכפל את מאגר GitHub משורת הפקודה.

git clone https://github.com/googlecodelabs/performance-analytics.git

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

אם יש לכם גישה לתוכנית דיפני, אתם יכולים להשתמש בה כדי לראות בדיוק מה השתנה מכל שלב.

בשיעור Lab זה, קחו אתכם קובץ HTML יחיד שטוען את הנכסים הבאים:

  • גופני אינטרנט
  • גיליונות סגנונות
  • תמונות Google
  • JavaScript

אתם גם צריכים לכתוב קוד חדש שמודד את מדדי הביצועים העיקריים של כל אחד מסוגי הנכסים האלה.

שיקולים הקשורים לביצועים של נכסים

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

שירות CSS

לדוגמה: גיליונות סגנונות חוסמים את הרינדור של כל הרכיבים ב-DOM שמגיעים אליהם, כלומר הדפדפן צריך לבקש בקשה לגיליון האלקטרוני, להוריד אותו ולנתח אותו לפני שניתן לעבד את כל התוכן ב-DOM שמופיע אחריו. מסיבה זו בדרך כלל כדאי למקם גיליונות סגנונות ב-<head> של המסמך. בשל אופי החסימה של ה-CSS, מומלץ גם למקם את שירות ה-CSS הקריטי רק ב-<head> לאחר מכן יש לטעון את שירות ה-CSS שאינו קריטי.

JavaScript

לעומת זאת, JavaScript אינו חוסם עיבוד, אך הוא חוסם את הניתוח והבנייה של ה-DOM. זה הכרחי מפני ש-JavaScript יכול לשנות את ה-DOM, כלומר בכל פעם שהדפדפן רואה תג <script> (לא כולל סקריפטים אסינכרוניים), הוא צריך להפעיל את הקוד לפני שממשיכים לתג הבא. אם התג <script> מאזכר קובץ JavaScript חיצוני, הוא צריך להוריד את הקוד ולהפעיל אותו לפני שממשיכים.

מסיבה זו מומלץ לרוב לטעון את ה-JavaScript ממש לפני תג </body> , כך שרוב ה-DOM זמין במהירות האפשרית.

גופנים באינטרנט

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

תמונות

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

השלב הראשון בשיעור 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>

לאחר מכן, פותחים את שרת האינטרנט של Chrome ומפעילים שרת מקומי בספרייה שיצרתם. מקפידים לסמן את הכתובת "הצגה אוטומטית של index.html"

צילום מסך 2016-05-11 בשעה 23:43

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

צילום מסך ב-11.5.2016 בשעה 10.59.03

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

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

קובץ ההדגמה שיצרת כרגע מכיל את שירות ה-CSS הבא, המתייחס ל-Bottstrap ולמספר סגנונות מוטבעים.

<!-- 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 Timing API, יש דרך קלה הרבה יותר: השיטה performance.mark.

כדי לסמן מתי שירות ה-CSS יסיים את העיבוד וגם את ההפעלה של הסקריפט, צריך להוסיף את שורת הקוד הבאה מייד לפני התגובה הסוגרת <!-- End CSS -->.

<script>performance.mark('css:unblock');</script>

השיטה performance.mark יוצרת חותמת זמן ברזולוציה גבוהה בנקודת הזמן הזו, ומשייכת אותה לכל שם שהועבר לשיטה. במקרה כזה, נתתם לסימון את הסימן "css:unblock"

השיטה performance.mark עוברת ביד עם השיטה performance.measure, המשמשת לחישוב הפרש הזמן בין שני סימנים (בנוסף לסימנים שיוצרים, אפשר גם להשתמש בסימני הדפדפן שנוצרו באופן אוטומטי עבור הנקודות השונות ב-Navigation Timing API).

פונקציית הכלי הבאה מודדת ומחזירה את משך הזמן שעובר בין הסימן&הוספת 39; לבין הסימן 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) ולהעתיק את הקוד שלמעלה.

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

לאחר הכתיבה של פונקציה כדי לקרוא לקוד זה, קובץ ה-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.

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

צילום מסך 2016-05-17 בשעה 11.13.22

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

<!-- 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 בלי להוריד בפועל את הגופן. כלומר, אם מוסיפים סימן באמצעות תג סקריפט בתוך השורה אל ה-DOM מיד לאחר שהגופן&lt3link;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" בקריאה החוזרת (callback) הפעילה, כך שאפשר למדוד את משך זמן הטעינה של הגופנים.
  • התג <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.

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

צילום מסך ב-17.5.2016 בשעה 11.13.22

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

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

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

לאחר הורדת תמונה לפני ההוספה שלה ל-DOM, הנקודה שבה היא הופכת לגלויה היא הנקודה שבה היא מופיעה ב-DOM. מצד שני, אם תמונה לא הורדה לפני שהיא נוספה ל-DOM, הנקודה שבה היא הופכת לגלויה היא כשה-handler של onload מופעל.

לכן, כדי לדעת מתי תמונה גלויה, עליך לטפל בשני המקרים.

ניתן לעשות זאת על ידי הוספת סימנים בכל handler של טעינה של תמונה ובתג <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.

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

צילום מסך ב-17.5.2016 בשעה 11.13.39

מכיוון שתגי <script> ללא המאפיין async חוסמים את ניתוח ה-DOM עד שהמערכת מורידה ומריץה אותם, אפשר לקבוע באיזו נקודה כל הסקריפטים יסתיימו ביצוע על ידי הוספה של סימן בתג סקריפט מוטבע מיד אחרי ה-<script> הסינכרוני ב-DOM.

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

כדי לעקוב אחר כל טעינה של 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 ו-Boststrap'ירדו וסיימו את הביצוע.

כדי למדוד כמה זמן נמשך התהליך, יש להוסיף את הפונקציה הבאה ל-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.

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

צילום מסך 2016-05-17 בשעה 11.14.03 בבוקר

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

כדי להתמודד עם כך, ניתן להשתמש בזיהוי תכונות. מוסיפים את קטע הקוד שבהמשך לפני קטע הגופנים. השורה הזו של 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.

השלב האחרון במעבדה זו הוא להעביר את הנתונים שנרשמים למסוף ולשלוח אותם במקום זאת ל-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> אסינכרוני שיוריד את ספריית ה-JavaScript analytics.js.
  • מפעיל פונקציית 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.

כדי להביא בחשבון את ה-getchas האלה, צריך לעדכן את הצהרת ההחזרה בפונקציה measurementDuration כדי לעגל את ערך ההחזרה:

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>

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

צילום מסך 2016-05-10 בשעה 18.57.23

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

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

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

צילום מסך 2016-05-10 בשעה 18.54.13

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

דיווח על הנתונים שאתם אוספים

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

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

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

צילום מסך 2016-05-10 בשעה 19:16

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

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

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

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

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

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

  • האם הערכים של המדדים שנמצאים במגמת ירידה או עלייה לאורך זמן?
  • איך השימוש במטמון במטמון דרך קובץ שירות (service worker) או אחסון מקומי ישפיע על הביצועים הכוללים של האתר?
  • האם המשאבים שלכם נטענים באופן אופטימלי? כלומר, האם יש פער גדול בין ההורדה של המשאב לבין הזמן שבו הוא זמין לשימוש?
  • האם יש קשרים בין הביצועים למדדים אחרים שאחריהם מתבצע מעקב (למשל, שיעור ההרשמה, משך הביקור באתר, רכישות וכו')?

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