Mesurer l'impact réel des service workers sur les performances

L'un des principaux avantages des service workers (du moins du point de vue des performances) est leur capacité à contrôler de manière proactive la mise en cache des ressources. Une application Web capable de mettre en cache toutes ses ressources nécessaires doit se charger beaucoup plus rapidement pour les visiteurs connus. Mais à quoi ressemblent ces gains pour les utilisateurs réels ? Et comment mesurer cela ?

L'application Web Google I/O (ou IOWA) est une application Web progressive qui exploite la plupart des nouvelles fonctionnalités offertes par les service workers pour offrir à ses utilisateurs une expérience riche et semblable à celle d'une application. L'entreprise s'est également servie de Google Analytics pour recueillir des données de performance clés et des schémas d'utilisation auprès de son audience large et variée d'utilisateurs.

Cette étude de cas explique comment l'IOWA a utilisé Google Analytics pour répondre à des questions clés sur les performances et créer des rapports sur l'impact réel des service workers.

En commençant par les questions

Chaque fois que vous mettez en œuvre des analyses dans un site Web ou une application, il est important de commencer par identifier les questions auxquelles vous essayez de répondre à partir des données que vous allez collecter.

Bien que nous souhaitions répondre à plusieurs questions, pour les besoins de cette étude de cas, concentrons-nous sur deux des plus intéressantes.

1. La mise en cache des service workers est-elle plus performante que les mécanismes de mise en cache HTTP existants disponibles dans tous les navigateurs ?

Nous nous attendons déjà à ce que les pages se chargent plus rapidement pour les visiteurs connus que pour les nouveaux visiteurs, car les navigateurs peuvent mettre en cache les demandes et les diffuser instantanément lors des visites répétées.

Les service workers offrent d'autres fonctionnalités de mise en cache qui permettent aux développeurs de contrôler précisément la mise en cache et le mode de mise en cache. Dans le cadre de l'IOWA, nous avons optimisé l'implémentation de nos service workers pour mettre en cache chaque élément, afin que les visiteurs connus puissent utiliser l'application entièrement hors connexion.

Mais cet effort serait-il plus efficace que ce que fait déjà le navigateur par défaut ? Et si oui, dans quelle mesure ? 1

2. Quel est l'impact du service worker sur l'expérience de chargement du site ?

En d'autres termes, quelle est la vitesse à laquelle le site se charge, indépendamment des temps de chargement réels mesurés par les métriques traditionnelles de chargement des pages ?

Répondre aux questions sur ce que ressent une expérience n'est évidemment pas une tâche facile, et aucune métrique ne représentera parfaitement un tel sentiment subjectif. Cela dit, certaines métriques sont sans aucun doute meilleures que d'autres. Il est donc important de choisir les bonnes.

Choisir la bonne métrique

Par défaut, Google Analytics suit le temps de chargement des pages (via l'API Navigation Timing) pour 1% des visiteurs d'un site et rend ces données disponibles par le biais de métriques telles que le temps moyen et le temps de chargement de la page.

Pos. Le temps de chargement de la page est une bonne métrique pour répondre à notre première question, mais ce n'est pas particulièrement utile pour répondre à la seconde. Tout d'abord, l'événement load ne correspond pas nécessairement au moment où l'utilisateur peut réellement interagir avec l'application. En outre, deux applications présentant exactement le même temps de chargement peuvent avoir l'impression de se charger très différemment. Par exemple, un site doté d'un écran de démarrage ou d'un indicateur de chargement a l'impression de se charger beaucoup plus rapidement qu'un site qui n'affiche qu'une page vierge pendant plusieurs secondes.

Dans l'IOWA, nous avons montré une animation de compte à rebours sur l'écran de démarrage qui, à mon avis, a très bien servi à divertir l'utilisateur pendant que le reste de l'application se chargeait en arrière-plan. Pour cette raison, il est beaucoup plus logique de suivre le temps nécessaire pour que l'écran de démarrage s'affiche pour mesurer les performances de chargement perçues. Pour obtenir cette valeur, nous avons choisi la métrique time to first paint.

Une fois que nous avons déterminé les questions auxquelles nous voulions répondre et identifié les métriques qui nous permettraient d'y répondre, il était temps d'implémenter Google Analytics et de commencer à mesurer les résultats.

Mise en œuvre des données analytiques

Si vous avez déjà utilisé Google Analytics, vous connaissez probablement l'extrait de code de suivi JavaScript recommandé. Elle se présente comme suit :

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

La première ligne du code ci-dessus initialise une fonction ga() globale (si elle n'existe pas déjà), et la dernière ligne télécharge la bibliothèque analytics.js de manière asynchrone.

La partie centrale contient les deux lignes suivantes:

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

Ces deux commandes permettent d'effectuer le suivi des pages consultées par les internautes qui consultent votre site, mais pas beaucoup plus. Si vous souhaitez effectuer le suivi d'autres interactions utilisateur, vous devez le faire vous-même.

Pour l'IOWA, nous voulions suivre deux éléments supplémentaires:

  • Temps écoulé entre le début du chargement de la page et l'affichage des pixels à l'écran.
  • Indique si un service worker contrôle la page. Grâce à ces informations, nous avons pu segmenter nos rapports afin de comparer les résultats avec et sans service worker.

Capturer le temps nécessaire à la première peinture

Certains navigateurs enregistrent l'heure précise à laquelle le premier pixel s'affiche à l'écran, et mettent ce temps à la disposition des développeurs. Cette valeur, comparée à la valeur navigationStart exposée via l'API Navigation Timing, nous permet de comptabiliser très précisément le temps écoulé entre le moment où l'utilisateur a demandé la page pour la première fois et le moment où il a vu quelque chose pour la première fois.

Comme je l'ai déjà mentionné, il est important de mesurer le délai de chargement des pages (time to first paint), car il s'agit du premier point à partir duquel un utilisateur découvre la vitesse de chargement de votre site. C'est la première impression que les utilisateurs obtiennent. Une bonne première impression peut avoir un impact positif sur le reste de l'expérience utilisateur2.

Pour obtenir la valeur First Paint dans les navigateurs qui l'expose, nous avons créé la fonction utilitaire 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;
    }
  }
}

Cela nous permet d'écrire une autre fonction qui envoie un événement de non-interaction avec comme valeur le délai de première peinture (time to first paint)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
    });
  }
}

Une fois ces deux fonctions écrites, notre code de suivi se présente comme suit:

// 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();

Notez que selon le moment où le code ci-dessus est exécuté, les pixels peuvent ou non avoir déjà été peints à l'écran. Pour que nous exécutions toujours ce code après le premier enregistrement, nous avons reporté l'appel à sendTimeToFirstPaint() jusqu'à l'événement load. En fait, nous avons décidé de reporter l'envoi de toutes les données d'analyse jusqu'à ce que la page ait été chargée pour que ces demandes ne concurrencent pas le chargement d'autres ressources.

// 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();
});

Le code ci-dessus génère un rapport firstpaint fois à Google Analytics, mais ce n'est que la moitié du chemin. Nous devions quand même suivre l'état du service worker. sinon nous ne serons pas en mesure de comparer le premier temps de rendu d'une page contrôlée par un agent du service à celui d'une page non contrôlée.

Déterminer l'état d'un service worker

Pour déterminer l'état actuel du service worker, nous avons créé une fonction utilitaire qui renvoie l'une des trois valeurs suivantes:

  • contrôlé: un service worker contrôle la page. Dans le cas de l'IOWA, cela signifie également que tous les éléments ont été mis en cache et que la page fonctionne hors connexion.
  • supported: le navigateur est compatible avec un service worker, mais celui-ci ne contrôle pas encore la page. Il s'agit de l'état attendu pour les nouveaux visiteurs.
  • unsupported: le navigateur de l'utilisateur n'est pas compatible avec le service worker.
function getServiceWorkerStatus() {
  if ('serviceWorker' in navigator) {
    return navigator.serviceWorker.controller ? 'controlled' : 'supported';
  } else {
    return 'unsupported';
  }
}

Cette fonction a obtenu pour nous l'état du service worker : l'étape suivante consistait à associer cet état aux données que nous envoyions à Google Analytics.

Suivi des données personnalisées à l'aide de dimensions personnalisées

Par défaut, Google Analytics vous propose de nombreuses façons de subdiviser votre trafic total en groupes, en fonction des attributs de l'utilisateur, de la session ou de l'interaction. Ces attributs sont appelés dimensions. Les développeurs Web s'intéressent généralement aux dimensions telles que Navigateur, Système d'exploitation ou Catégorie d'appareils.

L'état du service worker n'est pas une dimension fournie par défaut par Google Analytics. En revanche, Google Analytics vous permet de créer vos propres dimensions personnalisées et de les définir comme vous le souhaitez.

Pour l'IOWA, nous avons créé une dimension personnalisée appelée Service Worker Status et défini sa portée sur hit (c'est-à-dire par interaction)4. Chaque dimension personnalisée que vous créez dans Google Analytics reçoit un index unique au sein de cette propriété. Dans votre code de suivi, vous pouvez référencer cette dimension par son index. Par exemple, si l'index de la dimension que nous venons de créer est de 1, nous pouvons mettre à jour notre logique comme suit pour envoyer l'événement firstpaint afin d'inclure l'état du 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()
});

Cela fonctionne, mais seul l'état du service worker sera associé à cet événement spécifique. L'état du service worker étant potentiellement utile pour toute interaction, nous vous recommandons de l'inclure dans toutes les données envoyées à Google Analytics.

Pour inclure ces informations dans tous les appels (par exemple, les pages vues, les événements, etc.), nous définissons la valeur de la dimension personnalisée sur l'objet tracker lui-même, avant d'envoyer des données à Google Analytics.

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

Une fois définie, cette valeur est envoyée avec tous les appels suivants pour le chargement de la page en cours. Si l'utilisateur charge à nouveau la page plus tard, la fonction getServiceWorkerStatus() renvoie probablement une nouvelle valeur, qui est alors définie sur l'objet de suivi.

Remarque rapide sur la clarté et la lisibilité du code: étant donné que les autres personnes qui consultent ce code ne savent peut-être pas à quoi renvoie dimension1, il est toujours préférable de créer une variable qui associe des noms de dimensions pertinents aux valeurs qu'analytics.js utilisera.

// 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();
});

Comme indiqué précédemment, l'envoi de la dimension État du service worker avec chaque appel nous permet de l'utiliser dans les rapports sur n'importe quelle métrique.

Comme vous pouvez le constater, près de 85% de toutes les pages vues pour l'IOWA provenaient de navigateurs compatibles avec le service worker.

Les résultats: répondre à nos questions

Une fois que nous avons commencé à collecter des données pour répondre à nos questions, nous avons pu créer un rapport sur ces données pour voir les résultats. (Remarque: toutes les données Google Analytics présentées ici représentent le trafic Web réel vers le site de l'IOWA du 16 au 22 mai 2016.)

La première question que nous nous posions était la suivante: la mise en cache des service workers est-elle plus performante que les mécanismes de mise en cache HTTP existants disponibles dans tous les navigateurs ?

Pour répondre à cette question, nous avons créé un rapport personnalisé qui traite de la statistique Pos. les temps de chargement des pages en fonction de différentes dimensions. Cette métrique est adaptée à cette question, car l'événement load ne se déclenche qu'après le téléchargement de toutes les ressources initiales. Il reflète donc directement le temps de chargement total pour toutes les ressources critiques du site5.

Nous avons choisi les dimensions suivantes:

  • Notre dimension personnalisée État du service worker.
  • Type d'utilisateur : indique s'il s'agit de la première visite de l'utilisateur sur le site ou s'il y revient. Remarque: aucune ressource ne sera mise en cache pour un nouveau visiteur, contrairement aux visiteurs connus.
  • Catégorie d'appareil, qui permet de comparer les résultats sur mobile et sur ordinateur.

Pour éviter que des facteurs non liés au service worker faussent nos résultats de temps de chargement, nous avons limité notre requête aux navigateurs compatibles avec les service workers.

Comme vous pouvez le constater, les visites de notre application contrôlées par un service worker se chargent beaucoup plus rapidement que les visites non contrôlées, y compris celles d'utilisateurs connus qui ont probablement mis en cache la plupart des ressources de la page. Il est également intéressant de noter qu'en moyenne, les visiteurs utilisant un mobile avec un service worker enregistrent des chargements plus rapides que les nouveaux visiteurs sur ordinateur.

"...les visites de notre application lorsqu'elles sont contrôlées par un service worker se chargent beaucoup plus rapidement que les visites non contrôlées..."

Vous pouvez obtenir plus de détails dans les deux tableaux suivants:

Durée Temps de chargement de la page (ordinateur)
État du service worker Type d'utilisateur Temps de chargement moyen de la page (ms) Taille de l'échantillon
A contrôlé Visiteur connu 2568 30860
Compatible Visiteur connu 3612 1289
Compatible Nouveau visiteur 4664 21991
Durée Temps de chargement de la page (mobile)
État du service worker Type d'utilisateur Temps de chargement moyen de la page (ms) Taille de l'échantillon
A contrôlé Visiteur connu 3760 8162
Compatible Visiteur connu 4843 676
Compatible Nouveau visiteur 6158 5779

Vous vous demandez peut-être comment un visiteur connu dont le navigateur prend en charge un service worker peut se trouver dans un état non contrôlé. Plusieurs explications sont possibles:

  • L'utilisateur a quitté la page lors de la première visite avant que le service worker n'ait pu terminer l'initialisation.
  • L'utilisateur a désinstallé le service worker à l'aide des outils pour les développeurs.

Ces deux situations sont relativement rares. Nous pouvons le constater dans les données en examinant les valeurs Exemple de chargement de page dans la quatrième colonne. Notez que les lignes du milieu ont un échantillon beaucoup plus petit que les deux autres.

La deuxième question était la suivante: Quel est l'impact d'un service worker sur l'expérience de chargement du site ?

Pour répondre à cette question, nous avons créé un autre rapport personnalisé pour la métrique Pos. Valeur de l'événement et filtrer les résultats pour n'inclure que nos événements firstpaint. Nous avons utilisé les dimensions Catégorie d'appareils et notre dimension personnalisée État du service worker.

Contrairement à ce à quoi je m'attendais, le service worker sur mobile avait beaucoup moins d'impact sur le délai de chargement initial de la page que sur le chargement global de la page.

"... le service worker sur mobile a eu beaucoup moins d'impact sur le délai de chargement initial de la page que sur le chargement global de la page."

Pour comprendre pourquoi c'est le cas, nous devons analyser les données plus en détail. Les moyennes peuvent être utiles pour les vues d'ensemble générales et les grandes lignes, mais pour bien comprendre comment ces chiffres se répartissent entre différents utilisateurs, nous devons examiner une distribution de firstpaint fois.

Obtenir la distribution d'une métrique dans Google Analytics

Pour obtenir la répartition des firstpaint fois, nous avons besoin d'accéder aux résultats individuels de chaque événement. Malheureusement, Google Analytics ne nous facilite pas la tâche.

Google Analytics nous permet de ventiler un rapport en fonction de la dimension de notre choix, mais pas de détailler un rapport par métrique. Cela ne veut pas dire que c'est impossible, mais cela signifie simplement que nous avons dû personnaliser un peu plus notre implémentation pour obtenir le résultat souhaité.

Étant donné que les résultats du rapport ne peuvent être ventilés que par dimension, nous avons dû définir la valeur de la métrique (dans ce cas, firstpaint fois) en tant que dimension personnalisée pour l'événement. Pour ce faire, nous avons créé une autre dimension personnalisée appelée Valeur de la métrique et modifié notre logique de suivi firstpaint, comme suit:

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);
  }
}

Pour le moment, l'interface Web de Google Analytics ne permet pas de visualiser la distribution de valeurs de métriques arbitraires. Toutefois, grâce à l'API Core Reporting de Google Analytics et à la bibliothèque Google Charts, nous pouvons interroger les résultats bruts et créer nous-mêmes un histogramme.

Par exemple, la configuration de requête API suivante a été utilisée pour obtenir la distribution des valeurs firstpaint sur ordinateur avec un service worker non contrôlé.

{
  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'
    }
  ]
}

Cette requête API renvoie un tableau de valeurs semblable à celui-ci. Remarque: il ne s'agit que des cinq premiers résultats. Les résultats sont triés du plus petit au plus grand, ces lignes représentent donc les temps les plus rapides.

Résultats de la réponse de l'API (cinq premières lignes)
ga:dimension2 ga:totalEvents
4 3
5 2
6 10
7 8
8 10

Voici la signification de ces résultats en français:

  • Dans 3 événements, la valeur de firstpaint était de 4 ms
  • Deux événements ont une valeur de firstpaint de 5 ms
  • Il y a eu 10 événements où la valeur de firstpaint était de 6 ms
  • Huit événements ont une valeur de firstpaint de 7 ms.
  • Il y a eu 10 événements où la durée de la value de firstpaint était de 8 ms
  • etc.

À partir de ces résultats, nous pouvons extrapoler la valeur firstpaint pour chaque événement et créer un histogramme de la distribution. Nous l'avons fait pour chacune des requêtes que nous avons exécutées.

Voici à quoi ressemblait la distribution sur ordinateur avec un service worker non contrôlé (mais compatible) :

Délai avant la première distribution de Paint sur ordinateur (compatible)

La durée médiane de firstpaint pour la distribution ci-dessus est de 912 ms.

La forme de cette courbe est assez typique de la répartition des temps de chargement. Comparez cela avec l'histogramme ci-dessous, qui montre la distribution des premiers événements de peinture pour les visites au cours desquelles un service worker contrôlait la page.

Délai avant la première distribution du dessin sur ordinateur (contrôlé)

Notez que lorsqu'un service worker contrôlait la page, de nombreux visiteurs ont subi un premier rendu quasi immédiat, avec une médiane de 583 ms.

"... lorsqu'un service worker contrôlait la page, de nombreux visiteurs ont subi un premier repeindre presque immédiat..."

Pour mieux comprendre la comparaison de ces deux distributions, le graphique suivant présente une vue fusionnée des deux. L'histogramme des visites de service workers non contrôlés est superposé à l'histogramme des visites contrôlées. Les deux sont superposés sur un histogramme combiné.

Délai avant la première distribution des peintures sur ordinateur

Une chose que j'ai trouvée intéressante à propos de ces résultats, c'est que la distribution avec un service worker contrôlé présentait toujours une courbe en forme de cloche après le pic initial. Je m'attendais à un pic initial important, puis à une traînée progressive. Je ne m'attendais pas à un deuxième pic sur la courbe.

En cherchant la cause de ce problème, j'ai découvert que même si un service worker contrôle une page, son thread peut être inactif. Cela permet au navigateur d'économiser des ressources : à l'évidence, il n'est pas nécessaire que tous les service workers de chaque site visité soient actifs et prêts à tout moment. Cela explique la fin de la distribution. Pour certains utilisateurs, le démarrage du thread de service worker a pris du retard.

Toutefois, comme le montre la distribution, les navigateurs avec service worker livrent le contenu plus rapidement que les navigateurs passant par le réseau.

Voici comment les choses s'affichaient sur mobile:

Délai avant distribution First Paint sur mobile

Bien que nous ayons encore constaté une augmentation considérable du nombre de premiers temps de peinture quasi immédiats, la queue était un peu plus grosse et plus longue. Cela est probablement dû au fait que, sur mobile, le démarrage d'un thread de service worker inactif prend plus de temps que sur ordinateur. Cela explique aussi pourquoi la différence entre la durée moyenne de firstpaint n'était pas aussi importante que je m'attendais à ce que je m'y attendais (voir ci-dessus).

"...sur mobile, le démarrage d'un thread de service worker inactif prend plus de temps que sur ordinateur."

Voici la répartition de ces variations de temps médians de première peinture sur mobile et ordinateur, regroupée par statut de prestataire de service:

Délai médian avant la première peinture (ms)
État du service worker Ordinateur Mobile
A contrôlé 583 1634
Compatible (non contrôlé) 912 1933

Ces visualisations de répartition ont demandé un peu plus de temps et d'efforts que de créer un rapport personnalisé dans Google Analytics. Toutefois, elles nous donnent une bien meilleure idée de la manière dont les service workers affectent les performances de notre site que les seules moyennes.

Autre impact des service workers

Outre l'impact sur les performances, les service workers améliorent l'expérience utilisateur de plusieurs autres manières mesurables par Google Analytics.

Accès hors connexion

Les service workers permettent aux utilisateurs d'interagir avec votre site hors connexion. Bien qu'une assistance hors connexion soit probablement essentielle pour toute progressive web app, déterminer son degré d'importance dans votre cas dépend en grande partie de l'utilisation hors connexion. Mais comment mesurer cela ?

L'envoi de données à Google Analytics nécessite une connexion Internet, mais il n'est pas nécessaire que les données soient transmises au moment exact de l'interaction. Google Analytics permet d'envoyer des données d'interaction ultérieurement en spécifiant un décalage temporel (via le paramètre qt).

Depuis deux ans, l'IOWA utilise un script de service worker qui détecte les appels ayant échoué vers Google Analytics lorsque l'utilisateur est hors connexion et les relance plus tard avec le paramètre qt.

Pour savoir si l'utilisateur était en ligne ou hors connexion, nous avons créé une dimension personnalisée appelée En ligne et lui avons attribué la valeur navigator.onLine. Nous avons ensuite écouté les événements online et offline, et mis à jour la dimension en conséquence.

Pour nous faire une idée de la fréquence à laquelle il était courant pour un utilisateur d'être hors connexion lorsqu'il utilise l'IOWA, nous avons créé un segment qui ciblait les utilisateurs ayant réalisé au moins une interaction hors connexion. Il s'avère que cela représente près de 5% des utilisateurs.

Notifications push

Les service workers permettent aux utilisateurs d'activer la réception de notifications push. Dans l’IOWA, les utilisateurs étaient avertis lorsqu'une session de leur planning était sur le point de commencer.

Comme pour toute forme de notification, il est important de trouver le juste équilibre entre apporter de la valeur à l'utilisateur et le gêner. Pour mieux comprendre ce qui se passe, il est important de savoir si les utilisateurs acceptent de recevoir ces notifications, s'ils interagissent avec elles lorsqu'ils arrivent sur votre site, et si des utilisateurs qui ont déjà accepté de les recevoir modifient leurs préférences et se désinscrivent.

Dans l'IOWA, nous n'envoyions que des notifications liées au planning personnalisé de l'utilisateur, quelque chose que seuls les utilisateurs connectés pouvaient créer. Cela limitait l'ensemble des utilisateurs qui pouvaient recevoir des notifications auprès des utilisateurs connectés (suivis au moyen d'une dimension personnalisée appelée Connecté) dont les navigateurs acceptaient les notifications push (suivi au moyen d'une autre dimension personnalisée appelée Autorisation de notification).

Le rapport suivant est basé sur la métrique Utilisateurs et notre dimension personnalisée "Autorisation de notification", segmenté en fonction des utilisateurs qui se sont connectés à un moment donné et dont les navigateurs acceptent les notifications push.

Nous sommes ravis de constater que plus de la moitié des utilisateurs connectés ont choisi de recevoir des notifications push.

Bannières incitant à installer une application

Si une application Web de progression répond aux critères et est utilisée fréquemment par un utilisateur, celui-ci peut voir une bannière d'installation d'application l'invitant à ajouter l'application à son écran d'accueil.

Dans l'IOWA, nous avons suivi la fréquence à laquelle ces invites étaient présentées à l'utilisateur (et si elles étaient acceptées) à l'aide du code suivant:

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
    });
  });
});

Parmi les utilisateurs qui ont vu une bannière incitant à installer une application, environ 10% ont choisi de l'ajouter à leur écran d'accueil.

Améliorations possibles du suivi (pour la prochaine fois)

Les données analytiques que nous avons collectées auprès de l'IOWA cette année ont été inestimables. Mais avec le recul, il y a toujours des failles et des opportunités d'amélioration. Après avoir terminé l'analyse de cette année, voici deux choses que j'aurais aimé faire différemment que les lecteurs qui souhaitent mettre en œuvre une stratégie similaire pourraient envisager:

1. Suivre plus d'événements liés à l'expérience de chargement

Nous avons suivi plusieurs événements correspondant à une métrique technique (par exemple, HTMLImportsLoaded, WebComponentsReady, etc.), mais comme une grande partie du chargement a été effectué de manière asynchrone, le moment où ces événements se sont déclenchés ne correspondait pas nécessairement à un moment particulier du chargement global.

Le principal événement lié au chargement dont nous n'avons pas pu effectuer le suivi (alors que nous aurions dû l'avoir) est le moment où l'écran de démarrage a disparu et que l'utilisateur a pu voir le contenu de la page.

2. Stocker l'ID client d'analyse dans IndexedDB

Par défaut, analytics.js stocke le champ ID client dans les cookies du navigateur. Malheureusement, les scripts de service worker ne peuvent pas accéder aux cookies.

Cela nous a posé problème lorsque nous avons essayé d'implémenter le suivi des notifications. Nous voulions envoyer un événement à partir du service worker (via le protocole de mesure) chaque fois qu'une notification était envoyée à un utilisateur, puis suivre le succès du réengagement de cette notification si l'utilisateur avait cliqué dessus et revenait dans l'application.

Nous avons pu suivre l'efficacité des notifications de manière générale via le paramètre de campagne utm_source, mais nous ne sommes pas en mesure d'associer une session de réengagement particulière à un utilisateur particulier.

Pour contourner cette limitation, nous aurions pu stocker l'ID client via IndexedDB dans notre code de suivi, puis cette valeur aurait été accessible au script du service worker.

3. Autorisez le service worker à indiquer l'état en ligne/hors connexion.

L'inspection de navigator.onLine vous indique si votre navigateur peut se connecter au routeur ou au réseau local, mais elle ne vous indique pas nécessairement si l'utilisateur dispose d'une connectivité réelle. Étant donné que le script du service worker d'analyse hors connexion s'est contenté de relire les appels ayant échoué (sans les modifier ni les marquer comme ayant échoué), nous étions probablement sous-évalués.

À l'avenir, nous devrions suivre l'état de navigator.onLine et savoir si l'appel a été relancé par le service worker en raison d'une défaillance initiale du réseau. Cela nous donnera une idée plus précise de l'utilisation réelle hors connexion.

Conclusion

Cette étude de cas montre que l'utilisation d'un service worker améliore les performances de chargement de l'application Web Google I/O sur un large éventail de navigateurs, de réseaux et d'appareils. Il a également été démontré que lorsque vous examinez une répartition des données de charge sur un large éventail de navigateurs, de réseaux et d'appareils, vous obtenez beaucoup plus d'informations sur la façon dont cette technologie gère des scénarios réels et vous découvrez des caractéristiques de performances auxquelles vous n'aviez peut-être pas pensé.

Voici quelques-uns des principaux points à retenir de l'étude de l'IOWA:

  • En moyenne, les pages se chargeaient beaucoup plus rapidement lorsqu'un service worker contrôlait la page qu'en l'absence d'un service worker, tant pour les nouveaux visiteurs que pour les visiteurs connus.
  • Les visites des pages contrôlées par un service worker se chargent presque instantanément pour de nombreux utilisateurs.
  • Lorsqu'ils étaient inactifs, les service workers ont mis un peu de temps à démarrer. Toutefois, un service worker inactif est toujours plus performant qu'aucun service worker.
  • Le temps de démarrage d'un service worker inactif était plus long sur mobile que sur ordinateur.

Bien que les gains de performances observés dans une application particulière soient généralement utiles à la communauté des développeurs dans son ensemble, il est important de se rappeler que ces résultats sont spécifiques au type de site IOWA (site événementiel) et au type d'audience de l'IOWA (principalement les développeurs).

Si vous implémentez un service worker dans votre application, il est important d'implémenter votre propre stratégie de mesure afin d'évaluer vos propres performances et d'éviter toute régression future. Si c'est le cas, veuillez partager vos résultats pour que tout le monde puisse en bénéficier !

Notes de bas de page

  1. Il ne serait pas tout à fait juste de comparer les performances de l'implémentation du cache de notre service worker à celles de notre site en utilisant uniquement le cache HTTP. Dans l'optique d'optimiser l'IOWA pour les service workers, nous n'avons pas passé beaucoup de temps à optimiser le cache HTTP. Si nous avions eu, les résultats auraient probablement été différents. Afin d'en savoir plus sur l'optimisation de votre site pour le cache HTTP, consultez Optimiser votre contenu de manière efficace.
  2. Selon la façon dont votre site charge ses styles et son contenu, il est possible que le navigateur puisse peindre avant que le contenu ou les styles ne soient disponibles. Dans ce cas, firstpaint peut correspondre à un écran blanc vide. Si vous utilisez firstpaint, il est important de vous assurer qu'il correspond à un point significatif du chargement des ressources de votre site.
  3. Techniquement, nous pourrions envoyer un appel de type timing (sans interaction par défaut) pour capturer ces informations au lieu d'un événement. D'ailleurs, les appels temporels ont été ajoutés dans Google Analytics spécifiquement pour suivre les métriques de charge comme celle-ci : Toutefois, les appels temporels sont fortement échantillonnés au moment du traitement, et leurs valeurs ne peuvent pas être utilisées dans les segments. Compte tenu de ces limitations actuelles, les événements indépendants de toute interaction sont plus adaptés.
  4. Pour mieux comprendre la portée d'une dimension personnalisée dans Google Analytics, consultez la section Dimension personnalisée dans le centre d'aide Analytics. Il est également important de comprendre le modèle de données de Google Analytics, qui se compose d'utilisateurs, de sessions et d'interactions (appels). Pour en savoir plus, regardez le cours d'Analytics Academy sur le modèle de données Google Analytics.
  5. Cela ne tient pas compte des ressources chargées de manière différée après l'événement de chargement.