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 éléments. Une application Web capable de mettre en cache toutes ses ressources nécessaires devrait se charger beaucoup plus rapidement pour les visiteurs connus. Mais à quoi ressemblent ces gains réellement pour de vrais utilisateurs ? Et comment mesurer cela ?

L'application Web Google I/O (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, semblable à celle d'une application. L'entreprise a également utilisé Google Analytics pour recueillir des données de performances clés et des modèles d'utilisation provenant d'une audience vaste et variée.

Cette étude de cas explique comment l'IOWA s'est servi de Google Analytics pour répondre à des questions de performances clés et rendre compte de l'impact réel des service workers.

En commençant par les questions

Chaque fois que vous implémentez des données analytiques sur un site Web ou dans 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.

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

1. La mise en cache par service worker 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 de 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 le contenu et le mode de mise en cache. Dans l'IOWA, nous avons optimisé l'implémentation de notre service worker de sorte que chaque élément soit mis en cache, afin que les visiteurs connus puissent utiliser l'application hors connexion.

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

2. Quel est l'impact du service worker sur le chargement du site ?

En d'autres termes, quelle est la vitesse de chargement du site, quels que soient les temps de chargement réels mesurés par les métriques de chargement de page classiques ?

Répondre aux questions sur la sensation d'une expérience n'est évidemment pas une tâche facile, et aucune métrique ne peut représenter parfaitement un sentiment aussi subjectif. Cela dit, certaines métriques sont certainement 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 met ces données à disposition via des métriques telles que "Temps de chargement moyen de la page".

La métrique Temps de chargement moyen de la page est utile pour répondre à la première question, mais elle 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 interagir avec l'application. De plus, deux applications ayant exactement le même temps de chargement peuvent avoir l'impression de se charger beaucoup différemment. Par exemple, un site avec un écran de démarrage ou un indicateur de chargement donnera probablement l'impression qu'il se charge beaucoup plus rapidement qu'un site qui n'affiche qu'une page vierge pendant plusieurs secondes.

Dans l'IOWA, nous avons affiché une animation de compte à rebours sur l'écran de démarrage qui, à mon avis, était très efficace pour divertir l'utilisateur pendant que le reste de l'application se chargeait en arrière-plan. C'est pourquoi le suivi du temps nécessaire pour que l'écran de démarrage s'affiche est beaucoup plus logique pour mesurer les performances de chargement perçues. Nous avons choisi la métrique time to first paint (Temps d'exécution du premier rendu) pour obtenir cette valeur.

Une fois que nous avons défini les questions auxquelles nous voulions répondre et identifié les métriques qui pourraient nous être utiles pour y répondre, il est temps d'implémenter Google Analytics et de commencer à effectuer des mesures.

La mise en œuvre de l'analyse

Si vous avez déjà utilisé Google Analytics, vous connaissez probablement l'extrait de suivi JavaScript recommandé. Il 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 ces deux lignes:

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

Ces deux commandes permettent d'identifier les pages que les utilisateurs consultent sur 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 souhaitions nous pencher sur deux éléments supplémentaires:

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

Durée de capture de la première peinture

Certains navigateurs enregistrent l'heure précise à laquelle le premier pixel apparaît à l'écran, et ils mettent cette heure à la disposition des développeurs. Cette valeur, comparée à la valeur navigationStart présentée via l'API Navigation Timing, nous permet de déterminer de manière très précise le temps écoulé entre le moment où l'utilisateur a initialement demandé la page et le moment où il a vu quelque chose pour la première fois.

Comme je l'ai déjà mentionné, le délai de première visualisation est une mesure importante, car il s'agit du premier point à partir duquel un utilisateur constate la vitesse de chargement de votre site. C'est la première impression que les utilisateurs voient, et une bonne première impression peut avoir un impact positif sur le reste de l'expérience utilisateur2.

Pour obtenir la première valeur Paint dans les navigateurs qui l'offrent, 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;
    }
  }
}

Nous pouvons maintenant écrire une autre fonction qui envoie un événement non-interaction dont la valeur indique le temps de 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 s'exécute, il est possible que des pixels aient déjà été affichés à l'écran. Pour nous assurer de toujours exécuter ce code après le premier affichage, nous avons reporté l'appel à sendTimeToFirstPaint() jusqu'à la fin de l'événement load. En fait, nous avons décidé de différer l'envoi de toutes les données d'analyse jusqu'au chargement de la page, afin de nous assurer que ces demandes ne rivalisent pas avec 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 rapporte firstpaint fois à Google Analytics, mais ce n'est que la moitié de l'histoire. Nous devions toujours suivre l'état du service worker, sinon nous ne serions pas en mesure de comparer les premiers temps de rendu d'une page contrôlée par un service worker et 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 (compatible) : le navigateur est compatible avec le service worker, mais celui-ci ne contrôle pas encore la page. Il s'agit de l'état attendu pour les nouveaux visiteurs.
  • unsupported (non compatible) : 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 l'état du service worker. L'étape suivante consistait à associer cet état aux données que nous envoyions à Google Analytics.

Effectuer le suivi des données personnalisées à l'aide de dimensions personnalisées

Par défaut, Google Analytics vous offre de nombreuses méthodes pour 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 dimensions qui intéressent le plus les développeurs Web, comme Navigateur, Système d'exploitation ou Catégorie d'appareil.

L'état du service worker n'est pas une dimension fournie par défaut par Google Analytics. Cependant, 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 État du service worker et défini son champ d'application sur hit (par interaction)4. Chaque dimension personnalisée que vous créez dans Google Analytics se voit attribuer 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 était 1, nous pourrions 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 particulier. Il peut être utile de connaître l'état du service worker dans le cas de toute interaction. Il est donc préférable de l'inclure dans toutes les données envoyées à Google Analytics.

Pour inclure ces informations dans tous les appels (par exemple, toutes les pages vues, tous les événements, etc.), nous définissons la valeur de la dimension personnalisée au niveau de 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 page en cours. Si l'utilisateur charge à nouveau la page ultérieurement, la fonction getServiceWorkerStatus() renverra probablement une nouvelle valeur, et cette valeur sera définie sur l'objet de suivi.

Remarque rapide sur la clarté et la lisibilité du code: comme les personnes qui consultent ce code ne savent peut-être pas à quoi dimension1 fait référence, il est toujours préférable de créer une variable qui met en correspondance les noms de dimension significatifs avec les valeurs utilisées par 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();
});

Comme indiqué précédemment, l'envoi de la dimension État du service worker avec chaque appel nous permet de l'utiliser dans la création de 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 les service workers.

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 des rapports sur ces données pour voir les résultats. (Remarque: toutes les données Google Analytics affiché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 étions posée était la suivante: La mise en cache par les service worker 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é portant sur la métrique Temps de chargement moyen des pages pour différentes dimensions. Cette métrique est particulièrement adaptée à cette question, car l'événement load ne se déclenche qu'une fois que toutes les ressources initiales ont été téléchargées. Il reflète donc directement le temps de chargement total de toutes les ressources critiques du site5.

Les dimensions que nous avons choisies sont les 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 revient. (Remarque: aucune ressource ne sera mise en cache pour un nouveau visiteur, contrairement à un visiteur connu.)
  • Catégorie d'appareil, qui nous permet de comparer les résultats sur mobile et sur ordinateur.

Pour éliminer la possibilité que des facteurs non liés aux service workers 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 sont chargées beaucoup plus rapidement que les visites non contrôlées, même celles provenant d'utilisateurs connus qui avaient 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 service worker ont constaté des chargements plus rapides que les nouveaux visiteurs sur ordinateur.

"...les visites de notre application contrôlées par un service worker se sont chargées bien plus vite que les visites non contrôlées..."

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

Temps de chargement moyen de la page (ordinateur)
État du service worker User Type 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
Temps de chargement moyen de la page (mobile)
État du service worker User Type 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 il est possible qu'un visiteur connu dont le navigateur accepte un service worker ne soit jamais contrôlé. Voici quelques explications possibles:

  • L'utilisateur a quitté la page lors de la première visite avant que le service worker ait eu la possibilité de terminer l'initialisation.
  • L'utilisateur a désinstallé le service worker via les outils pour les développeurs.

Ces deux situations sont relativement rares. Nous pouvons le constater dans les données en examinant les valeurs de l'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.

Deuxième question: Quel est l'impact du service worker sur le chargement du site ?

Pour répondre à cette question, nous avons créé un autre rapport personnalisé pour la métrique Valeur moyenne des événements et filtré les résultats afin de n'inclure que les é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 a eu beaucoup moins d'impact sur le temps de la première peinture que sur le chargement global de la page.

"... un service worker sur mobile a eu beaucoup moins d'impact sur le temps de chargement des pages que sur le chargement global de la page."

Pour en comprendre les raisons, nous devons analyser les données de manière plus approfondie. Les moyennes sont utiles pour les résumés généraux et les termes généraux, mais pour bien comprendre comment ces chiffres se répartissent entre un éventail d'utilisateurs, nous devons examiner une répartition de firstpaint fois.

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

Pour obtenir la distribution des firstpaint fois, nous devons accéder aux résultats individuels pour chaque événement. Malheureusement, Google Analytics ne vous facilite pas la tâche.

Google Analytics nous permet de ventiler les données d'un rapport selon les dimensions de notre choix, mais pas de ventiler les rapports par métriques. Cela ne veut pas dire que c'est impossible, mais simplement que nous avons dû personnaliser davantage notre implémentation pour obtenir le résultat souhaité.

Étant donné que les résultats du rapport ne peuvent être ventilés que par dimensions, nous avons dû définir la valeur de la métrique (dans ce cas, firstpaint fois) en tant que dimension personnalisée au niveau de l'événement. Pour ce faire, nous avons créé une autre dimension personnalisée, appelée Valeur de la métrique, et mis à jour 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 principale de création de rapports de Google Analytics et à la bibliothèque de graphiques Google, nous avons pu 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 une 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 ressemblant à ceci (remarque: il ne s'agit que des cinq premiers résultats). Les résultats sont triés du plus petit au plus grand, de sorte que ces lignes représentent 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 ce que signifient ces résultats en français:

  • Pour trois événements, la valeur firstpaint était de 4 ms
  • Il y a eu 2 événements pour lesquels la valeur firstpaint était de 5 ms
  • Pour 10 événements, la valeur firstpaint était de 6 ms
  • Pour 8 événements, la valeur firstpaint était de 7 ms
  • Pour 10 événements, 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) :

Distribution du délai avant le premier rendu sur ordinateur (compatible)

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

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

Distribution de la première peinture sur ordinateur (contrôlé)

Notez que lorsqu'un service worker contrôlait la page, de nombreux visiteurs ont vu une première peinture presque immédiate, avec une durée médiane de 583 ms.

"... lorsqu'un service worker contrôlait la page, de nombreux visiteurs ont été exposés à la première peinture tout de suite..."

Pour mieux comprendre comment ces deux distributions se comparent l'une à l'autre, le graphique suivant présente une vue fusionnée des deux. L'histogramme présentant les visites non contrôlées des service workers se superpose à l'histogramme affichant les visites contrôlées. Les deux sont superposés sur un histogramme qui représente les deux combinées.

Distribution du temps de première peinture sur ordinateur

Une chose que j'ai trouvée intéressante dans 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, mais je ne m'attendais pas à un deuxième pic dans la courbe.

En examinant les causes possibles, j'ai découvert que même si un service worker contrôle une page, son thread peut être inactif. Le navigateur effectue cette opération pour économiser des ressources. De toute évidence, tous les service workers que vous avez visités ne doivent pas nécessairement être actifs et prêts en un clin d'œil. Ceci explique la fin de la distribution. Pour certains utilisateurs, un retard s'est produit lors du démarrage du thread de service worker.

Toutefois, comme vous pouvez le constater dans la distribution, même avec ce délai initial, les navigateurs avec service worker ont fourni le contenu plus rapidement que les navigateurs passant par le réseau.

Voici comment s'affichent les résultats sur mobile:

Délai de distribution du premier appliquer sur mobile

Même si nous observons encore une augmentation significative des temps de première peinture presque immédiats, la queue était un peu plus grande 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 également pourquoi la différence entre la durée moyenne de firstpaint n'était pas aussi importante que ce que je pensais (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 par service worker à partir de ces variantes du temps médian de chargement des données sur mobile et sur ordinateur:

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

Bien que ces visualisations de distribution prennent un peu plus de temps et d'efforts que la création d'un rapport personnalisé dans Google Analytics, elles nous donnent une bien meilleure idée de la façon dont les service workers affectent les performances de notre site que les moyennes seules.

Autre impact des service workers

Outre l'impact sur les performances, les service workers ont un impact sur l'expérience utilisateur de plusieurs autres façons, qui peuvent être mesurées avec Google Analytics.

Accès hors connexion

Les service workers permettent aux utilisateurs d'interagir avec votre site en mode hors connexion. Même s'il est sans doute essentiel d'assurer une compatibilité hors connexion pour toute progressive web app, le fait de déterminer son importance dans votre cas dépend en grande partie de la quantité d'utilisation qui s'effectue hors connexion. Mais comment mesurer cela ?

L'envoi de données à Google Analytics nécessite une connexion Internet, mais pas au moment exact de l'interaction. Google Analytics permet d'envoyer a posteriori les données d'interaction 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é à 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 l'avons définie sur la valeur navigator.onLine. Nous avons ensuite écouté les événements online et offline, et mis à jour la dimension en conséquence.

Pour savoir à quel point il est fréquent qu'un utilisateur soit hors connexion lorsqu'il utilise l'IOWA, nous avons créé un segment qui ciblait les utilisateurs ayant généré au moins une interaction hors connexion. Il s'avère que cela représentait près de 5% des utilisateurs.

Notifications push

Les service workers permettent aux utilisateurs d'accepter de recevoir des notifications push. Dans l'IOWA, les utilisateurs étaient avertis lorsqu'une session de leur planning était sur le point de commencer.

Comme pour toutes les formes de notifications, il est important de trouver le bon équilibre entre une valeur ajoutée pour l'utilisateur et une gêne. Pour mieux comprendre ce qui se passe, il est important de savoir si les utilisateurs acceptent de recevoir ces notifications, s'ils interagissent avec eux à leur arrivée, et si des utilisateurs qui ont déjà accepté de les recevoir ont changé leurs préférences et ne les ont pas activées.

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 le nombre d'utilisateurs susceptibles de recevoir des notifications pour les utilisateurs connectés (suivi via une dimension personnalisée appelée Connecté) dont les navigateurs étaient compatibles avec les notifications push (suivi effectué au moyen d'une autre dimension personnalisée appelée Autorisation de notification).

Le rapport suivant s'appuie sur la métrique Utilisateurs et sur notre dimension personnalisée "Autorisation de notification". Il est 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 les notifications push.

Bannières incitant à installer une application

Si une application Web de progression répond aux critères et est fréquemment utilisée par un utilisateur, une bannière d'installation peut s'afficher, 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 s'ils é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 d'installation d'application, environ 10% ont choisi de l'ajouter à leur écran d'accueil.

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

Les données d'analyse que nous avons recueillies cette année avec l'IOWA ont été inestimables. Mais avec le recul, il y a toujours des lacunes et des opportunités d'améliorer les choses pour la prochaine fois. À la fin de cette année d'analyse, voici deux choses que j'aurais aimé faire différemment et qui pourraient intéresser les lecteurs souhaitant mettre en place une stratégie similaire:

1. Suivre d'autres événements liés au chargement

Nous avons suivi plusieurs événements correspondant à une métrique technique (par exemple, HTMLImportsLoaded, WebComponentsReady, etc.). Toutefois, comme une grande partie du chargement s'effectuait de manière asynchrone, le moment auquel ces événements se sont déclenchés ne correspondait pas nécessairement à un moment précis du chargement global.

Le principal événement lié au chargement que nous n'avons pas suivi (mais que nous aurions aimé) est le moment où l'écran de démarrage a disparu et 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 de l'ID client dans les cookies du navigateur. Malheureusement, les scripts de service worker n'ont pas accès aux cookies.

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

Bien que nous ayons réussi à suivre l'efficacité des notifications en général via le paramètre de campagne utm_source, nous n'avons pas pu associer une session de réengagement particulière à un utilisateur en particulier.

Pour contourner cette limitation, nous aurions pu stocker l'ID client via IndexedDB dans notre code de suivi. Le script du service worker a alors accès à cette valeur.

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

L'inspection de navigator.onLine vous permet de savoir si votre navigateur peut se connecter au routeur ou au réseau local, mais cela ne permet pas nécessairement de déterminer si vous disposez d'une véritable connectivité. Et comme notre script de service worker d'analyse hors connexion revoyait simplement les appels ayant échoué (sans les modifier ni les marquer comme ayant échoué), nous avions probablement sous-estimé notre utilisation hors connexion.

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

Conclusion

Cette étude de cas a montré que l'utilisation d'un service worker a permis d'améliorer 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 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 comprenez bien comment cette technologie gère des scénarios réels et vous découvrez des caractéristiques de performances auxquelles vous ne vous attendiez peut-être pas.

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

  • En moyenne, les pages se chargent beaucoup plus vite lorsqu'un service worker la contrôlait que sans le service worker, tant pour les visiteurs nouveaux que connus.
  • Visites de pages contrôlées par un service worker chargées presque instantanément pour de nombreux utilisateurs.
  • Lorsqu'ils étaient inactifs, les service workers mettent 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 à signaler à la communauté de développeurs dans son ensemble, il est important de se rappeler que ces résultats sont spécifiques au type de site IOWA (site d'événements) et au type d'audience de l'IOWA (principalement des 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, partagez vos résultats afin que tout le monde puisse en bénéficier.

Notes de bas de page

  1. Il n'est pas tout à fait juste de comparer les performances de l'implémentation du cache de notre service worker à celles de notre site en n'utilisant que le cache HTTP. Comme nous optimisions IOWA pour les service workers, nous n'avons pas passé beaucoup de temps à optimiser le cache HTTP. Si nous avions fait cela, les résultats auraient probablement été différents. Pour en savoir plus sur l'optimisation de votre site pour le cache HTTP, consultez la page Optimiser le 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 soit en mesure de 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, assurez-vous qu'il correspond à un moment pertinent du chargement des ressources de votre site.
  3. Techniquement, nous pourrions envoyer un appel de durée (qui ne sont pas interactifs par défaut) pour capturer ces informations au lieu d'un événement. En réalité, les appels temporels ont été ajoutés à Google Analytics spécifiquement pour suivre les métriques de chargement de ce type. Toutefois, ils sont fortement échantillonnés au moment du traitement, et leurs valeurs ne peuvent pas être utilisées dans les segments. Compte tenu des limitations actuelles, les événements indépendants de toute interaction restent mieux adaptés.
  4. Pour mieux comprendre la portée d'une dimension personnalisée dans Google Analytics, consultez la section Dimension personnalisée du centre d'aide Analytics. Il est également important de comprendre le modèle de données Google Analytics, qui comprend les utilisateurs, les sessions et les interactions (appels). Pour en savoir plus, regardez le cours Analytics Academy sur le modèle de données Google Analytics.
  5. Ce paramètre ne tient pas compte des ressources chargées en différé après l'événement de chargement.