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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

אחרי שהחלטנו על השאלות שרצינו לענות עליהן וזיהינו את המדדים שיעזרו לנו לענות עליהן, הגיע הזמן להטמיע את 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 לבין התוצאות ללא Service Worker.

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

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

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

"…ביקורים באפליקציה שלנו שנשלטו על ידי קובץ שירות נטענו הרבה יותר מהר מאשר ביקורים שלא נשלטו…"

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

"…כשקובץ שירות (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% מהמשתמשים.

התראות

שירותי עבודה מאפשרים למשתמשים להביע הסכמה לקבלת התראות בדחיפה. ב-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 שומרת את השדה client ID בקובצי ה-cookie של הדפדפן. לצערנו, ל-scripts של שירותי העבודה אין גישה לקובצי cookie.

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

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

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

3. לאפשר לקובץ השירות לדווח על סטטוס אופליין/אונליין

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

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

סיכום

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

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

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