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

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

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

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

מתחילים עם השאלות

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

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

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

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

שירותי ה-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) שולט בדף זה או לא. בעזרת המידע הזה נוכל לפלח את הדוחות שלנו כדי להשוות בין התוצאות עם שירות העבודה לבין התוצאות ללא שירות העבודה.

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

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

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

כדי לקבל את ערך ה-paint הראשון בדפדפנים שמציגים אותו, יצרנו את פונקציית השירות 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 שולט בדף. במקרה של IOWA, המשמעות היא גם שכל הנכסים הושהו במטמון והדף פועל במצב אופליין.
  • נתמך: הדפדפן תומך ב-Service Worker אבל ה-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, יצרנו מאפיין מותאם אישית שנקרא סטטוס של שירות לעבודה ברקע והגדרתנו את ההיקף שלו כ-היט (כלומר לכל אינטראקציה).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.

כדי לכלול את המידע הזה בכל ההיטים (למשל, כל צפיות בדפים, אירועים וכו'), אנחנו מגדירים את ערך המאפיין המותאם אישית באובייקט tracker עצמו, לפני ששולחים נתונים ל-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) סוג המשתמש זמן ממוצע של טעינת דף (אלפיות השנייה) גודל הדגימה
שליטה ב: אורח חוזר 2568 30860
נתמך אורח חוזר 3612 1289
נתמך מבקר חדש 4664 21991
זמן טעינה ממוצע של דף (נייד)
סטטוס קובץ השירות (service worker) סוג המשתמש זמן ממוצע של טעינת דף (אלפיות השנייה) גודל הדגימה
שליטה ב: אורח חוזר 3760 8162
נתמך אורח חוזר 4843 676
נתמך מבקר חדש 6158 5779

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

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

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

השאלה השנייה שלנו הייתה: איך שירות ה-worker משפיע על חוויית הטעינה של האתר?

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

בניגוד למה שציפיתי, ל-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 היה 4ms
  • היו 2 אירועים שבהם הערך של firstpaint היה 5 אלפיות השנייה
  • היו 10 אירועים שבהם הערך של firstpaint היה 6 אלפיות השנייה
  • היו 8 אירועים שבהם הערך של firstpaint היה 7 אלפיות השנייה
  • היו 10 אירועים שבהם הערך של firstpaint value היה 8 אלפיות שנייה
  • וכו'

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

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

חלוקת הזמן להצגת תוכן ראשוני במחשב (נתונים נתמכים)

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

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

חלוקת הזמן עד להצגת תוכן ראשוני במחשב (שליטה)

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

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

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

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

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

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

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

כך זה נראה בנייד:

חלוקת הזמן עד להצגת התוכן העיקרי בנייד

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

"…בנייד, התחלת חוט של שירות פעיל נמשכת יותר זמן מאשר במחשב."

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

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

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

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

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

גישה אופליין

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

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

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

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

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

התראות

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

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

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

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

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

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

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

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

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