La performance web est l’une des problématiques les plus importantes lors du développement d’un site. Si vous en doutez, Amazon a déjà constaté que 100ms de chargement supplémentaire de leurs pages causaient … 1% de chiffre d’affaire en moins.
Si les sites e-commerces sont directement impactés par une valeur mesurable (le chiffe d’affaire donc), la navigation mobile (bien améliorée avec l’arrivée du responsive webdesign) nous place à nouveau face aux problèmes de débit. La 4G c’est bien beau, mais la majeure partie des mobinautes peuvent encore surfer en 3G, et encore…
Bref, je ne rappelle pas ici les principes de base de la webperf, mais propose de mettre en place des alertes automatiques pour palier aux retards et oublis de la prise en compte de certaines bonnes pratiques.
Phantomas
Beaucoup de monde connait déjà webpagetest, qui aide depuis de nombreuses années les développeurs à analyser les sites et à en optimiser les performances. Mais malgré son avantage de réaliser les tests sur de vrais navigateurs, avec de véritables latences réseau et restrictions de débit, il n’est que peu automatisable dans le cadre d’une intégration continue.
Depuis quelques temps, Phantomas permet lui aussi de récupérer tout un tas de métriques relatives à la performance d’un site (et pas seulement) via phantomjs. Installable via npm, on peut très aisément y faire appel en ligne de commande ou outil de build (grunt/gulp/…).
Installation et exemple d’utilisation
npm install --sav-dev phantomas
Pour essayer, c’est très simple.
phantomas http://google.fr
Et niveau résultat, on se retrouve avec une bonne grosse centaine de métriques (extrait – liste) :
phantomas v1.11.0 metrics for <http://www.google.fr/>:
* requests: 9
* gzipRequests: 0
* postRequests: 0
* httpsRequests: 0
* notFound: 0
* bodySize: 6040 bytes
* contentLength: 112268 bytes
* httpTrafficCompleted: 1782 ms
* timeToFirstByte: 145 ms
* timeToLastByte: 245 ms
* ajaxRequests: 0
* htmlCount: 3
* htmlSize: 787 bytes
* cssCount: 0
* cssSize: 0 bytes
* jsCount: 1
* jsSize: 986 bytes
* jsonCount: 0
* jsonSize: 0 bytes
* imageCount: 4
* imageSize: 110495 bytes
[...]
Appeler phantomas depuis une tâche gulp
Alors tout ça c’est chouette, mais c’est une validation manuelle, trop souvent oubliée ou tardive. Aggréger les scripts et les minifier une veille de mise en production, c’est théoriquement safe, mais je n’aime pas trop tenter le destin.
Du coup, une validation automatique, c’est quand même plus sûr. Surtout si elle est mise en place dès le début du projet. Je vous fait la version Gulp, parce que j’aime bien.
var gulp = require('gulp'),
phantomas = require('phantomas');
gulp.task('phantomas', function(cb) {
phantomas('http://google.fr', {}, function(code, json, results) {
console.log(json.metrics.requests); // Total des requêtes
cb();
});
});
Pourquoi ne pas utiliser/proposer un plugin gulp pour ça ? Il existe bien quelque chose – gulp-louis -, mais Gulp propose cet avantage, par rapport à grunt par exemple, d’être configuré via du code. Phantomas étant un module node, on n’a pas besoin d’une couche supplémentaire qui ne servirait qu’à appeler phantomas. C’est la façon de faire que propose notamment Karma.
Pour être un peu plus sexy
Bon, de façon un peu plus complète et configurable, on peut décider de valider certaines métriques face à des seuils définis dans le projet. Libre à chacun de faire comme il l’entend, mais je proposerai quelques principes :
- Se limiter au niveau du nombre de métriques surveillées. 10, c’est déjà très bien.
- Avoir plusieurs niveaux d’alerte : trivial, important, bloquant…
- Ne pas avoir peur de casser le build en retournant une erreur si certains points sont bloquants (une page de plus de 10 Mo est une très bonne raison)
Voici un petit exemple de ce qu’on pourrait avoir, en deux parties : le gulpfile, et une petite configuration json.
// Gulpfile.js
var gulp = require('gulp'),
phantomas = require('phantomas'),
colors = require('colors');
gulp.task('phantomas', function(cb) {
var config = require('./.phantomasrc.json');
phantomas(config.url, config.options, function(code, json, results) {
var failed = false;
for (var metric in config.thresholds) {
var message = metric + ' ('+ config.thresholds[metric].join(',') +'): ' + json.metrics[metric];
if (config.thresholds[metric][1] < json.metrics[metric]) {
console.error(message.bold.bgRed.white);
failed = true;
} else if (config.thresholds[metric][0] < json.metrics[metric]) {
console.warn(message.bold.bgYellow.white);
} else {
console.log(message.bold.green);
}
}
cb(failed ? 'Phantomas threshold exceeded' : undefined);
});
});
// .phantomasrc.json
{
"url": "http://google.fr",
"options": {},
"thresholds": {
"requests": [50,70],
"notFound": [0,0],
"timeToFirstByte": [500,2000],
"cssCount": [2, 5],
"jsCount": [3, 5]
}
}
On notera juste deux points : l’ajout du module colors, histoire d’egayer la sortie console, et le fail en cas de dépassement trop important.
Un petit pre-push pour la route ?
Histoire d’être vraiment certain de pas envoyer chez les copains (quand c’est pas directement en prod) du code peu performant, on peut opter pour la mise en place d’un script de pre-push – en plus d’une validation sur un Jenkins par exemple. Allez, en voici un :
// pre-push.sh.
#!/bin/sh
./node_modules/.bin/gulp phantomas
Et pour l’activer :
chmod +x pre-push.sh && ln -s ../../pre-push.sh .git/hooks/pre-push