מדידת ההשפעה של אנשי שירות (service worker) על הביצועים בעולם

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

אפליקציית האינטרנט Google I/O (IOWA) היא אפליקציית אינטרנט מתקדמת שמנצלת את רוב היכולות החדשות שמציעות Service Workers כדי לספק למשתמשים חוויה עשירה, דמוית אפליקציה. בנוסף, החברה נעזרה ב-Google Analytics כדי לתעד נתוני ביצועים ודפוסי שימוש מרכזיים מקהל המשתמשים הגדול והמגוון שלה.

מקרה לדוגמה שממחיש איך IOWA השתמשה ב-Google Analytics כדי לענות על שאלות מפתח לגבי ביצועים ולדווח על ההשפעה של קובצי שירות (service worker) בעולם האמיתי.

נתחיל מהשאלות

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

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

‫1. האם השמירה במטמון של Service Worker מניבה ביצועים טובים יותר מהמנגנונים הקיימים לשמירה במטמון של HTTP, שזמינים בכל הדפדפנים?

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

ב-Service Workers יש יכולות חלופיות של שמירה במטמון, שמאפשרות למפתחים שליטה מדויקת על אופן הביצוע של השמירה במטמון ועל אופן הפעולה שלה. ב-IOWA, ביצענו אופטימיזציה של ההטמעה של קובץ השירות (service worker) כך שכל נכס יישמר במטמון, כדי שמבקרים חוזרים יוכלו להשתמש באפליקציה לגמרי במצב אופליין.

אבל האם מאמץ זה יהיה טוב יותר ממה שהדפדפן כבר עושה כברירת מחדל? ואם כן, כמה טוב יותר? 1

‫2. איך קובץ השירות (service worker) משפיע על החוויה של טעינת האתר?

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

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

בחירת המדד הנכון

כברירת מחדל, מערכת Google Analytics עוקבת אחר זמני טעינה של דפים (דרך Navigation Timing API) עבור% 1 מהמבקרים באתר. בנוסף, היא הופכת את הנתונים לזמינים באמצעות מדדים כמו 'זמן טעינה ממוצע של דף'.

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

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

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

ההטמעה של ניתוח הנתונים

אם השתמשתם ב-Google Analytics בעבר, סביר להניח שאתם מכירים את קטע הקוד המומלץ למעקב של JavaScript. כך הוא נראה:

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

השורה הראשונה בקוד שלמעלה מאתחלת פונקציית ga() גלובלית (אם היא עדיין לא קיימת), והשורה האחרונה מורידה באופן אסינכרוני את ספריית analytics.js.

החלק האמצעי מכיל את שתי השורות הבאות:

ga('create', 'UA-XXXXX-Y', 'auto');
ga('send', 'pageview');

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

רצינו לעקוב אחרי שני דברים נוספים לגבי IOWA:

  • הזמן שעבר מרגע התחלת הטעינה של הדף לראשונה ועד להופעת פיקסלים על המסך.
  • האם ה-Service Worker שולט בדף או לא. על סמך המידע הזה יכולנו לפלח את הדוחות כדי להשוות בין התוצאות עם קובץ שירות (service worker) לבין התוצאות בלעדיו.

לכידת משך הזמן עד להצגת התמונה הראשונה

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

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

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

function getTimeToFirstPaintIfSupported() {
  // Ignores browsers that don't support the Performance Timing API.
  if (window.performance && window.performance.timing) {
    var navTiming = window.performance.timing;
    var navStart = navTiming.navigationStart;
    var fpTime;

    // If chrome, get first paint time from `chrome.loadTimes`.
    if (window.chrome && window.chrome.loadTimes) {
      fpTime = window.chrome.loadTimes().firstPaintTime * 1000;
    }
    // If IE/Edge, use the prefixed `msFirstPaint` property.
    // See http://msdn.microsoft.com/ff974719
    else if (navTiming.msFirstPaint) {
      fpTime = navTiming.msFirstPaint;
    }

    if (fpTime && navStart) {
      return fpTime - navStart;
    }
  }
}

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

function sendTimeToFirstPaint() {
  var timeToFirstPaint = getTimeToFirstPaintIfSupported();

  if (timeToFirstPaint) {
    ga('send', 'event', {
      eventCategory: 'Performance',
      eventAction: 'firstpaint',
      // Rounds to the nearest millisecond since
      // event values in Google Analytics must be integers.
      eventValue: Math.round(timeToFirstPaint)
      // Sends this as a non-interaction event,
      // so it doesn't affect bounce rate.
      nonInteraction: true
    });
  }
}

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

// Creates the tracker object.
ga('create', 'UA-XXXXX-Y', 'auto');

// Sends a pageview for the initial pageload.
ga('send', 'pageview');

// Sends an event with the time to first paint data.
sendTimeToFirstPaint();

לתשומת ליבך, בהתאם למועד שבו הקוד שלמעלה פועל, יכול להיות שפיקסלים כבר צובעו על המסך או לא. כדי לוודא שתמיד נריץ את הקוד הזה אחרי הצגת התמונה הראשונה, דחינו את השיחה ל-sendTimeToFirstPaint() עד אחרי האירוע load. למעשה, החלטנו לדחות את השליחה של כל נתוני הניתוח עד לאחר טעינת הדף, כדי להבטיח שהבקשות האלה לא יתחרו בטעינה של משאבים אחרים.

// Creates the tracker object.
ga('create', 'UA-XXXXX-Y', 'auto');

// Postpones sending any hits until after the page has fully loaded.
// This prevents analytics requests from delaying the loading of the page.
window.addEventListener('load', function() {
  // Sends a pageview for the initial pageload.
  ga('send', 'pageview');

  // Sends an event with the time to first paint data.
  sendTimeToFirstPaint();
});

הקוד שלמעלה מדווח ל-Google Analytics פעמיים (firstpaint), אבל זה רק חצי מהסיפור. עדיין היינו צריכים לעקוב אחר הסטטוס של קובץ השירות (service worker), אחרת לא יכולנו להשוות את זמני הציור הראשונים בין דף שנמצא בשליטת Service Worker לבין דף לא מבוקר.

קביעת הסטטוס של Service Worker

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

  • מבוקר: קובץ שירות (service worker) שולט בדף. במקרה של IOWA, המשמעות היא שכל הנכסים נשמרו והדף פועל במצב אופליין.
  • נתמך: הדפדפן תומך ב-Service Worker, אבל קובץ השירות עדיין לא שולט בדף. זהו הסטטוס הצפוי למבקרים שמבקרים לראשונה.
  • unsupported: דפדפן המשתמש לא תומך בקובץ שירות (service worker).
function getServiceWorkerStatus() {
  if ('serviceWorker' in navigator) {
    return navigator.serviceWorker.controller ? 'controlled' : 'supported';
  } else {
    return 'unsupported';
  }
}

הפונקציה הזו קיבלה את הסטטוס של קובץ השירות (service worker) עבורנו. השלב הבא היה לשייך את הסטטוס הזה לנתונים ששלחנו אל Google Analytics.

מעקב אחר נתונים מותאמים אישית באמצעות מאפיינים מותאמים אישית

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

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

עבור IOWA יצרנו מאפיין מותאם אישית שנקרא סטטוס של קובץ שירות (Service Worker Status) והגדרנו את ההיקף שלו להתאמה (כלומר, לכל אינטראקציה).4 כל מאפיין מותאם אישית שיוצרים ב-Google Analytics מקבל אינדקס ייחודי בתוך אותו נכס, ובקוד המעקב שלכם ניתן להפנות למאפיין הזה באמצעות האינדקס שלו. לדוגמה, אם האינדקס של המאפיין שיצר עכשיו הוא 1, אפשר לעדכן את הלוגיקה הבאה כדי לשלוח את האירוע firstpaint כך שיכלול את הסטטוס של קובץ השירות (service worker):

ga('send', 'event', {
  eventCategory: 'Performance',
  eventAction: 'firstpaint',
  // Rounds to the nearest millisecond since
  // event values in Google Analytics must be integers.
  eventValue: Math.round(timeToFirstPaint)
  // Sends this as a non-interaction event,
  // so it doesn't affect bounce rate.
  nonInteraction: true,

  // Sets the current service worker status as the value of
  // `dimension1` for this event.
  dimension1: getServiceWorkerStatus()
});

הפעולה הזו עובדת, אבל היא תשייך רק את הסטטוס של קובץ השירות (service worker) לאירוע המסוים הזה. סטטוס של Service Worker הוא פרט שעשוי להיות שימושי לכל אינטראקציה, ולכן מומלץ לכלול אותו עם כל הנתונים שנשלחים ל-Google Analytics.

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

ga('set', 'dimension1', getServiceWorkerStatus());

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

הערה קצרה לגבי בהירות וקריאות של הקוד: ייתכן שאנשים אחרים שמציגים את הקוד הזה לא יודעים למה מתייחס dimension1, ולכן מומלץ תמיד ליצור משתנה שממפה שמות מאפיינים משמעותיים לערכים ב-analytics.js.

// Creates a map between custom dimension names and their index.
// This is particularly useful if you define lots of custom dimensions.
var customDimensions = {
  SERVICE_WORKER_STATUS: 'dimension1'
};

// Creates the tracker object.
ga('create', 'UA-XXXXX-Y', 'auto');

// Sets the service worker status on the tracker,
// so its value is included in all future hits.
ga('set', customDimensions.SERVICE_WORKER_STATUS, getServiceWorkerStatus());

// Postpones sending any hits until after the page has fully loaded.
// This prevents analytics requests from delaying the loading of the page.
window.addEventListener('load', function() {
  // Sends a pageview for the initial pageload.
  ga('send', 'pageview');

  // Sends an event with the time to first paint data.
  sendTimeToFirstPaint();
});

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

כפי שניתן לראות שכמעט 85% מכל הצפיות בדף של IOWA היו מדפדפנים תומכים ב-Service Worker.

התוצאות: מענה על השאלות שלנו

אחרי שהתחלנו לאסוף נתונים כדי לענות על השאלות שלנו, נוכל לדווח על הנתונים האלה כדי לראות את התוצאות. (הערה: כל הנתונים של Google Analytics המוצגים כאן מייצגים תנועת גולשים בפועל באתר אל אתר IOWA בין 16 ל-22 במאי 2016).

השאלה הראשונה שהייתה לנו הייתה: האם השמירה של קובצי שירות (service worker) במטמון מניבה ביצועים טובים יותר מהמנגנונים הקיימים לשמירה במטמון של HTTP, שזמינים בכל הדפדפנים?

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

המאפיינים שבחרנו הם:

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

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

כפי שניתן לראות, ביקורים באפליקציה שלנו כשהם מבוקרים על ידי קובץ שירות (service worker) נטענו די מהר יותר מביקורים לא מבוקרים, אפילו אלה של משתמשים חוזרים שככל הנראה שמרו את רוב המשאבים של הדף במטמון. מעניין גם לשים לב שבממוצע, מבקרים בניידים עם Service Worker ראו טעינה מהירה יותר מאשר מבקרים חדשים במחשב.

"...ביקורים באפליקציה שלנו כאשר הם מבוקרים על ידי קובץ שירות (service worker) נטענו די מהר יותר מביקורים לא מבוקרים..."

אפשר לראות פרטים נוספים בשתי הטבלאות הבאות:

זמן ממוצע של טעינת דף (מחשב)
סטטוס קובץ השירות (service worker) סוג המשתמש זמן ממוצע של טעינת דף (אלפיות השנייה) גודל הדגימה
שליטה ב: אורח חוזר 2568 30860
נתמך אורח חוזר 3612 1289
נתמך מבקר חדש 4664 21991
זמן ממוצע של טעינת דף (בנייד)
סטטוס קובץ השירות (service worker) סוג המשתמש זמן ממוצע של טעינת דף (אלפיות השנייה) גודל הדגימה
שליטה ב: אורח חוזר 3760 8162
נתמך אורח חוזר 4843 676
נתמך מבקר חדש 6158 5779

ייתכן שאתה תוהה כיצד ייתכן שמבקר חוזר שהדפדפן שלו תומך ב-Service Worker יהיה במצב לא מבוקר. יש לכך כמה הסברים אפשריים:

  • המשתמש עזב את הדף בביקור הראשוני לפני של-service worker הייתה הזדמנות לסיים את האתחול.
  • המשתמש הסיר את קובץ השירות (service worker) באמצעות הכלים למפתחים.

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

השאלה השנייה שלנו הייתה: איך קובץ השירות (service worker) משפיע על החוויה של טעינת האתר?

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

בניגוד למה שציפיתי, ל-service worker בנייד הייתה הרבה פחות השפעה על הזמן שנדרש לצביעה ראשונה מאשר על הטעינה הכוללת של הדף.

"...service worker בנייד השיג הרבה פחות השפעה על הזמן עד להצגת התוכן הראשון בהשוואה לזמן הטעינה הכולל של הדף."

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

הצגת ההתפלגות של מדד ב-Google Analytics

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

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

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

var customDimensions = {
  SERVICE_WORKER_STATUS: 'dimension1',
  <strong>METRIC_VALUE: 'dimension2'</strong>
};

// ...

function sendTimeToFirstPaint() {
  var timeToFirstPaint = getTimeToFirstPaintIfSupported();

  if (timeToFirstPaint) {
    var fields = {
      eventCategory: 'Performance',
      eventAction: 'firstpaint',
      // Rounds to the nearest millisecond since
      // event values in Google Analytics must be integers.
      eventValue: Math.round(timeToFirstPaint)
      // Sends this as a non-interaction event,
      // so it doesn't affect bounce rate.
      nonInteraction: true
    }

    <strong>// Sets the event value as a dimension to allow for breaking down the
    // results by individual metric values at reporting time.
    fields[customDimensions.METRIC_VALUE] = String(fields.eventValue);</strong>

    ga('send', 'event', fields);
  }
}

ממשק האינטרנט של Google Analytics לא מאפשר כרגע להציג באופן חזותי את ההתפלגות של ערכי מדדים שרירותיים, אבל בעזרת Google Analytics Core Reporting API וספריית Google Charts, יכולנו להריץ שאילתות על התוצאות הגולמיות ולאחר מכן ליצור היסטוגרמה בעצמנו.

לדוגמה, ההגדרה הבאה של בקשות API שימשה לקבלת התפלגות של ערכי firstpaint במחשב עם קובץ שירות (service worker) לא מבוקר.

{
  dateRanges: [{startDate: '2016-05-16', endDate: '2016-05-22'}],
  metrics: [{expression: 'ga:totalEvents'}],
  dimensions: [{name: 'ga:dimension2'}],
  dimensionFilterClauses: [
    {
      operator: 'AND',
      filters: [
        {
          dimensionName: 'ga:eventAction',
          operator: 'EXACT',
          expressions: ['firstpaint']
        },
        {
          dimensionName: 'ga:dimension1',
          operator: 'EXACT',
          expressions: ['supported']
        },
        {
          dimensionName: 'ga:deviceCategory',
          operator: 'EXACT',
          expressions: ['desktop']
        }
      ],
    }
  ],
  orderBys: [
    {
      fieldName: 'ga:dimension2',
      orderType: 'DIMENSION_AS_INTEGER'
    }
  ]
}

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

תוצאות תגובת API (חמש השורות הראשונות)
ga:dimension2 ga:totalEvents
4 3
5 2
6 10
7 8
8 10

הנה פירוש התוצאות באנגלית פשוטה:

  • תועדו 3 אירועים שבהם הערך של firstpaint היה 4 אלפיות שנייה
  • תועדו 2 אירועים שבהם הערך של firstpaint היה 5 אלפיות השנייה
  • תועדו 10 אירועים שבהם הערך של firstpaint היה 6 אלפיות השנייה
  • היו 8 אירועים שבהם הערך של firstpaint היה 7 אלפיות השנייה
  • היו 10 אירועים שבהם value של firstpaint נמשך 8 אלפיות השנייה
  • וכו'

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

כך נראתה ההפצה במחשב עם קובץ שירות (service worker) לא מבוקר (אבל נתמך):

הזמן עד להפצת הצבע הראשון במחשב (נתמך)

הזמן החציוני של firstpaint להתפלגות שלמעלה הוא 912 אלפיות השנייה.

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

הזמן עד לחלוקת הצבע הראשון במחשב (מבוקר)

שימו לב שכאשר קובץ שירות (service worker) שלט בדף, מבקרים רבים נהנו מהצגת תמונה ראשונה כמעט מיידית, עם חציון של 583 אלפיות השנייה.

"...כש-service worker שלט בדף, הרבה מבקרים חוו הצגת תמונה ראשונה כמעט מיידית..."

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

הזמן שחלף עד הפצת הצבע הראשון במחשב

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

כשבדקתי מה יכולה להיות הסיבה לכך, גיליתי שלמרות שה-Service Worker יכול לשלוט בדף, ה-thread שלו יכול להיות לא פעיל. הדפדפן עושה זאת כדי לחסוך במשאבים – מובן שאין צורך שכל קובץ שירות (service worker) בכל אתר שביקרתם בו יהיה פעיל ומוכן בהתראה של רגע. זהו הסבר לזנב ההתפלגות. אצל חלק מהמשתמשים חל עיכוב בזמן שה-thread של worker הופעל.

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

כך נראו דברים בנייד:

הזמן שחלף עד הפצת הצבע הראשון בנייד

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

"...בנייד, הפעלת שרשור של קובץ שירות (service worker) לא פעיל נמשכת זמן רב יותר מאשר במחשב".

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

זמן חציוני עד להצגת תמונה ראשונה (אלפיות שנייה)
סטטוס קובץ השירות (service worker) מחשב נייד
שליטה ב: 583 1634
נתמך (לא בשליטת) 912 1933

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

השפעה אחרת של קובצי שירות (service worker)

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

גישה אופליין

קובצי שירות (service worker) מאפשרים למשתמשים לבצע אינטראקציה עם האתר במצב אופליין, ולמרות שתמיכה כלשהי אופליין היא כנראה חיונית לכל Progressive Web App, היכולת לקבוע עד כמה היא קריטית במקרה שלכם תלויה במידה רבה בכמות השימוש המתרחשת אופליין. אבל איך אנחנו מודדים זאת?

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

בשנתיים האחרונות, השתמש ב-IOWA בסקריפט של Service Worker שמזהה היטים שנכשלו ב-Google Analytics כשהמשתמש לא מחובר לאינטרנט ומפעיל אותם מחדש מאוחר יותר עם הפרמטר qt.

כדי לבדוק אם המשתמש היה אונליין או אופליין, יצרנו מאפיין מותאם אישית בשם אונליין והגדירו אותו לערך של navigator.onLine. לאחר מכן, הקשבנו לאירועים online ו-offline ועדכנו את המאפיין בהתאם.

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

התראות

קובצי שירות (service worker) מאפשרים למשתמשים להביע הסכמה לקבלת התראות. ב-IOWA, המשתמשים קיבלו הודעה כשסשן בלוח הזמנים שלהם עומד להתחיל.

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

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

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

טוב לראות שיותר ממחצית מהמשתמשים שלנו שמחוברים לחשבון שלנו בחרו לקבל התראות.

מודעות באנר להתקנת אפליקציה

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

ב-IOWA, איתרנו את התדירות שבה הבקשות האלה הוצגו למשתמש (ואם הן אושרו) באמצעות הקוד הבא:

window.addEventListener('beforeinstallprompt', function(event) {
  // Tracks that the user saw a prompt.
  ga('send', 'event', {
    eventCategory: 'installprompt',
    eventAction: 'fired'
  });

  event.userChoice.then(function(choiceResult) {
    // Tracks the users choice.
    ga('send', 'event', {
      eventCategory: 'installprompt',
      // `choiceResult.outcome` will be 'accepted' or 'dismissed'.
      eventAction: choiceResult.outcome,
      // `choiceResult.platform` will be 'web' or 'android' if the prompt was
      // accepted, or '' if the prompt was dismissed.
      eventLabel: choiceResult.platform
    });
  });
});

מתוך המשתמשים שראו באנר להתקנת האפליקציה, כ-10% בחרו להוסיף אותה למסך הבית.

שיפורים אפשריים במעקב (לפעם הבאה)

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

‫1. מעקב אחר אירועים נוספים שקשורים לחוויית הטעינה

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

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

‫2. אחסון מזהה הלקוח של Analytics ב-IndexedDB

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

הדבר הציבה לנו בעיה כשניסינו להטמיע מעקב אחר התראות. רצינו לשלוח אירוע מה-Service Worker (באמצעות Measurement Protocol) בכל פעם שנשלחת התראה למשתמש, ולאחר מכן לעקוב אחר ההצלחה של חידוש האינטראקציה אם המשתמש לחץ עליה והגיע בחזרה לאפליקציה.

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

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

3. מתן הרשאה ל-Service Worker לדווח על סטטוס אונליין/אופליין

בדיקת navigator.onLine תודיע לך אם הדפדפן יכול להתחבר לנתב או לרשת תקשורת מקומית, אבל לא בהכרח תהיה לך אפשרות לקבוע אם למשתמש יש קישוריות אמיתית. ומכיוון שהסקריפט של Service worker של ניתוח נתונים אופליין פשוט הפעיל מחדש היטים שנכשלו (בלי לשנות אותם או לסמן אותם ככושלים), סביר להניח שלא קיבלנו דיווח על השימוש שלנו אופליין.

בעתיד עלינו לעקוב גם אחרי הסטטוס של navigator.onLine וגם אם ההתאמה הופעלה מחדש על ידי קובץ השירות (service worker) עקב כשל ראשוני ברשת. כך נוכל לקבל תמונה מדויקת יותר לגבי השימוש האמיתי אופליין.

סיכום

מקרה לדוגמה זה הראה שהשימוש ב-Service Worker אכן שיפר את ביצועי העומסים של אפליקציית האינטרנט Google I/O במגוון רחב של דפדפנים, רשתות ומכשירים. הוא גם הראה שכאשר בוחנים את ההתפלגות של נתוני העומס במגוון רחב של דפדפנים, רשתות ומכשירים, מקבלים תובנות רבות יותר לגבי האופן שבו הטכנולוגיה מטפלת בתרחישים מהעולם האמיתי ומגלים מאפייני ביצועים שאולי לא ציפיתם להם.

הנה כמה מהמסקנות המרכזיות של מחקר IOWA:

  • בממוצע, דפים נטענים קצת יותר מהר כש-Service Worker שלט בדף בהשוואה לזה שבלי Service Worker, עבור מבקרים חדשים וחוזרים.
  • ביקורים בדפים שנשלטים על ידי קובץ שירות (service worker) נטענים באופן כמעט מיידי עבור משתמשים רבים.
  • כשה-Service Workers לא היו פעילים, נדרש קצת זמן כדי להפעיל אותם. למרות זאת, Service worker לא פעילים עדיין השיגו ביצועים טובים יותר מאשר אף קובץ שירות (service worker).
  • זמן ההפעלה של קובץ שירות (service worker) לא פעיל היה ארוך יותר בנייד מאשר במחשב.

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

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

הערות שוליים

  1. זה לא לגמרי הוגן להשוות בין ביצועי המטמון של ה-Service Worker לבין הביצועים של האתר שלנו באמצעות מטמון HTTP בלבד. מכיוון שביצענו אופטימיזציה של IOWA ל-Service Worker, לא השקענו הרבה זמן באופטימיזציה למטמון HTTP. אם היו לנו קמפיינים כאלה, סביר להניח שהתוצאות היו שונות. למידע נוסף על אופטימיזציה של האתר לשמירה על מטמון HTTP, כדאי לקרוא את המאמר אופטימיזציה יעילה של תוכן.
  2. בהתאם לאופן שבו האתר שלך טוען את הסגנונות והתוכן שלו, ייתכן שהדפדפן יוכל לצבוע לפני שהתוכן או הסגנונות היו זמינים. במקרים כאלה, יכול להיות שהסמל firstpaint יהיה מסך לבן ריק. אם משתמשים ב-firstpaint, חשוב לוודא שהוא תואם לנקודה משמעותית בטעינת משאבי האתר.
  3. מבחינה טכנית, אנחנו יכולים לשלוח היט תזמון (שכברירת מחדל הוא לא אינטראקציה) כדי לתעד את המידע הזה במקום אירוע. למעשה, היטים של תזמון נוספו ל-Google Analytics במיוחד כדי לעקוב אחר מדדי טעינה כאלו. עם זאת, היטים של תזמון נדגמים באופן משמעותי בזמן העיבוד, ולא ניתן להשתמש בערכים שלהם בפלחים. לאור המגבלות הנוכחיות האלה, אירועים ללא אינטראקציה עדיין מתאימים יותר.
  4. כדי להבין טוב יותר איזה היקף להגדיר מאפיין מותאם אישית ב-Google Analytics, ניתן לעיין בקטע מאפיין מותאם אישית במרכז העזרה של Analytics. חשוב גם להבין את מודל הנתונים של Google Analytics, שמורכב ממשתמשים, מסשנים ומאינטראקציות (היטים). למידע נוסף, מומלץ לצפות בשיעור של האקדמיה של Analytics בנושא מודל הנתונים של Google Analytics.
  5. דבר זה לא כולל משאבים שנטענים באופן מדורג אחרי אירוע הטעינה.