Les indicateurs habituels de performance web (Premier Octet, Speed Index…) sont très intéressants mais j’ai souvent besoin d’ajouter des marqueurs temporels personnalisés, basés sur des événements qui ont un sens du point de vue métier.
- le moment où un contenu (ou une fonctionnalité) spécifique est disponible pour les utilisateurs
- le début et la fin de l’affichage d’une animation de chargement (très utile dans les tunnels complexe)
- dans une page découpée en composants récupérant leur contenu dans une API, les temps d’affichage spécifiques de chaque composant
- …
J’utilise la User Timing API (voir sur MDN [EN]) pour insérer des timings dans la performance timeline du navigateur. Comme on peut récupérer tous ces indicateurs dans Dareboost ou Contentsquare Speed Analysis, je peux ensuite surveiller leur évolution sur la durée.
En utilisant l’API avec JavaScript, poser un marqueur est assez simple (et bien supporté).
Par exemple :
// Le truc dont vous voulez mesurer l'occurence
document.getElementById('content').classList.add('blue');
// Le marqueur temporel
performance.mark('mark-blue');
Vous pouvez ensuite récupérer la valeur dans les outils de développement de votre navigateur, en utilisant :
performance.getEntriesByType('mark');
Cependant, j’ai récemment1 réalisé quelque chose d’important. Le simple fait que le navigateur exécute du code JavaScript ne signifie pas que l’utilisateur puisse voir les résultats de cette exécution. Le navigateur doit, entre temps, mettre à jour l’affichage. Or, sa capacité à le faire dépend du code JavaScript qui suit le morceau de code qui nous intéresse…
Dans l’exemple suivant, j’injecte une boucle bloquante d’une seconde après mon timing de performance, ce qui bloque le rendu par le navigateur.
// Le truc dont vous voulez mesurer l'occurence
document.getElementById('content').classList.add('blue');
// The timestamp
performance.mark('mark-blue-sync');
// Un bout de code qui coûte des ressources au navigateur
// pendant 1 seconde
let n = performance.now();
while (performance.now() - n < 1000) {}
Bien sûr, dans la vraie vie, vous n’aurez pas une simple boucle. Vous aurez d’autres tâches qui prendront peut-être beaucoup de temps, ou peut-être pas. Vous ne pouvez pas le savoir et, même, vous ne pouvez pas le tester, car tout cela dépend du contexte client que vous ne contrôlez pas.
Pour contourner ce problème, nous pouvons utiliser requestAnimationFrame()
afin de demander au navigateur de poser notre timing juste avant le prochain rendu (la prochaine frame) :
// Le truc dont vous voulez mesurer l'occurence
document.getElementById('content').classList.add('blue');
performance.mark('mark-blue-sync');
// Le Custom Timing, avant la prochaine frame
window.requestAnimationFrame(() => {
performance.mark('mark-blue-frame');
});
// Un bout de code qui coûte des ressources au navigateur
// pendant 1 seconde
let n = performance.now();
while (performance.now() - n < 1000) {}
Si vous voulez voir comment ça fonctionne par vous-même, voici une page de test.
EN UN MOT
Utilisez l’API User Timings pour créer des Custom Timings liés à votre activité.
Encapsulez-les dans des callbacks requestAnimationFrame()
pour comprendre le comportement de l’interface, plutôt que l’exécution du code.
Si vous voulez avoir une idée de la latence de l’affichage, attachez deux marques : l’une après l’événement à suivre, l’autre dans un callback requestAnimationFrame()
.
-
En lisant « Performance metrics for blazingly fast web apps » [EN], de Conrad Irwin ↩