Dans un précédent article du blog, je parlais d'optimisation de la vitesse d'un blog WordPress. L'un des soucis rencontrés venait de Wordpress en soi, qui multiplie les fichiers javascripts chargés à tire larigot, quitte à les dupliquer inutilement. Après avoir quitté WordPress pour Ghost, j'ai finalement pu reprendre mes tentatives d'optimisation pour atteindre le fameux "100/100" de Google Pagespeed Insights... sur certaines pages uniquement. Laissez-moi partager cette formidable aventure avec vous !

Les limitations

Le 100/100 n'est valable que sur les pages d'index. À partir du moment où vous entrez sur un article, le score descend entre 98 et... beaucoup moins. Deux raisons à cela :

  1. Je n'ai pas encore activé le cache côté serveur pour le forum où sont hébergés les commentaires des articles. Du coup, le fichier qui contient les données des commentaires n'est pas mis en cache.
  2. Principal cheval de bataille de Google pour améliorer les performances web, les images ne sont parfois pas optimisées autant qu'elles le devraient.

Pour le premier point, ça va s'arranger. Il faut juste configurer le server NginX. Pour le second point, c'est plus difficile. Outre la difficulté d'emploi des algorithmes de compression sans perte suggérés par Google, le plus gros souci des images sur le web est de s'adapter à la connexion des gens et à la taille de leur affichage. La solution la plus courante, plutôt bien gérée par Wordpress, consiste à générer plusieurs versions de taille variable des images uploadées. Ghost en est à sa version 0.7.x, il ne faut donc pas être surpris si certaines fonctionnalités importantes ne sont pas de la partie. Parmi celles-ci, la gestion des images est assez catastrophique, puisqu'il n'est possible d'uploader qu'une seule version d'une image. Les "featured images" sont un exemple concret du problème, surtout avec un thème comme le mien : entre les versions miniatures de la page d'accueil et les versions plein écran des articles, impossibles de trouver une taille qui s'adapte à tout.

Par conséquent, soit vous optez pour de grandes images redimensionnées à la volée quitte à subir de lourdes pertes de performance, soit vous utilisez des versions qui piquent les yeux lorsqu'elles s'affichent en grand. Je ne peux qu'attendre une mise à jour de Ghost pour corriger cette erreur : soit celle qui ajoutera, nativement, la gestion des images, soit celle qui permettra de créer ses propres plugins (auquel cas, j'en développerai un pour gérer les images). Une autre possibilité, moins probable et requérant malgré tout le concours de Ghost, serait d'attendre l'implémentation officielle de la balise HTML5 "picture". Elle permet de spécifier différentes sources d'images en fonction de la taille de l'écran.

Le défi du CSS

Optimiser ce blog ne fut pas une mince affaire. Situons le problème. Vous avez (1) optimisé vos images, (2) minifié vos fichiers javascript et css, (3) activé la compression GZIP et le cache du côté serveur, (4) tapé sur les doigts de votre hébergeur pour qu'il améliore la vitesse de connexion. Vous soufflez enfin. Tous ces efforts vous ont permis d'obtenir une note supérieure à 90 sur Google Pagespeed Insights et ça, ça rend fier. Mais pas complètement : il reste encore ces derniers pourcentages obscurs, cachés derrière l'énigmatique conseil que voilà. "Optimisez l'affichage des styles CSS pour les URL suivantes". Quoi t'est-ce que ?

500 Caption
Vous aussi, vous le haïssez, ce message ?

Ce que Google essaie de vous dire, c'est que dans un monde parfait, il faudrait idéalement charger les propriétés CSS "secondaires" de manière différée. Entre autres, Google parle "de la ligne de flottaison" ((La ligne de flottaison est un terme de marine qui désigne la délimitation entre la partie immergée et la partie émergée d'un navire.)), la délimitation entre la partie immédiatement visible de votre site et celle qui se trouve en-dessous. Pour optimiser votre site, il faudrait donc (1) déterminer quelles sont les propriétés CSS qui sont nécessaires à l'affichage initial, (2) les charger en priorité et (3) retarder le chargement des propriétés "secondaires" via un code javascript.

Étape par étape

(1) L'identification des propriétés nécessaires à l'affichage initial de votre page est la partie la plus difficile. D'abord, parce que bon nombre de sites ne sont pas pensés dans l'optique d'une ligne de flottaison. On affiche le contenu d'un texte ou d'une liste d'articles dès le début de la page, donc l'intégralité du CSS doit être chargé. Ce n'est pas un hasard si le nouveau thème de ce blog vous propose, à chaque page, une image en plein écran avec quelques premières informations à lire pour vous faire les dents : cette stratégie permet une délimitation facile entre ce qui doit être chargé d'abord (les propriétés en lien avec le menu et les images en plein écran) et ce qui vient après (le contenu de la page à proprement parler). Ensuite, il n'est pas aisé de déterminer quelles sont les propriétés nécessaires pour l'affichage initial : on nage généralement dans un océan de classes CSS. Heureusement, certaines personnes ont développé des scripts JS que vous pouvez employer sur vos pages. En gros, il s'agit de passer en revue toutes les propriétés utilisées dans l'espace visible de votre fenêtre. Le résultat n'est pas parfait, mais il vous donnera une première idée du travail nécessaire. Suivez ce lien (en anglais) si ça vous intéresse.

(2) À partir de là, vous pouvez charger les propriétés CSS prioritaires. Google recommande généralement d'utiliser du inline-css, c'est-à-dire de coincer lesdites propriétés dans une balise <style> au sein du <head> de votre page. L'idée est d'éviter d'appeler deux fichiers CSS et de générer du trafic inutile sur Internet. L'un des problèmes de ce conseil a trait à la "maintenabilité" de votre code CSS : il est en effet de difficile de modifier un code CSS minifié, coincé dans votre code HTML. Si vous employez (et configurez) des outils professionnels et performants, vous pourrez automatiser le déploiement de votre code CSS prioritaire dans l'en-tête des pages HTML. À défaut, vous pouvez opter pour ma solution : maintenir une feuille de style parallèle, la minifier et copy/paster le résultat dans le header de votre page à chaque mise à jour.

(3) Retarder le chargement du CSS secondaire est plus trivial. Vous pouvez utiliser le code suivant, fourni par l'équipe derrière Pagespeed Insights (pensez à remplacer /url/feuille/de/style par l'URL de votre feuille de style, évidemment) :

var cb = function () {
    var l = document.createElement('link');
    l.rel = 'stylesheet';
    l.href = '/url/feuille/de/style';
    var h = document.getElementsByTagName('head')[0];
    h.parentNode.insertBefore(l, h);
};
var raf = requestAnimationFrame || mozRequestAnimationFrame ||
        webkitRequestAnimationFrame || msRequestAnimationFrame;
if (raf) raf(cb);
else window.addEventListener('load', cb);

En gros, ce bout de javascript vérifie que vos ressources initiales sont chargées avant d'ajouter la feuille de style spécifiée par la variable l. La conséquence logique de l'utilisation d'un tel code est que les quelques rares illuminés à désactiver javascript sur leurs navigateurs ne pourront pas charger votre feuille de style. Pour pallier ce problème, vous pouvez toujours employer une balise <noscript> dans le header de votre page et y charger ladite feuille.

Un certain prix à payer

Si ces changements apportent effectivement un gain de vitesse appréciable, ils comportent des limitations qui peuvent gêner. J'en vois trois :

  1. D'abord, il faut construire son site dans l'optique d'une ligne de flottaison. Parfois, cette séparation artificielle n'est pas désirable. Certaines pages de ce blog ne devraient pas proposer une image en plein écran (je pense notamment à la pagination). C'est un sacrifice pénible.
  2. Le chargement asynchrone de certaines ressources jugées "secondaires" n'est pas pertinent si vos utilisateurs utilisent le bouton "back" du navigateur. Vous pouvez faire l'expérience avec ce blog : ouvrez une autre page, puis revenez sur celle-ci. Votre curseur de scrolling vous ramènera, par défaut, au bas de la page et les éléments qui s'y trouvent prendront quelques secondes à se charger. Il ne s'agit pas d'un défaut critique, mais ces détails gênent l'expérience utilisateur. Gagner quelques microsecondes de chargement vaut-il vraiment la peine ?
  3. Finalement, d'un point de vue plus technique, aucune mise en cache ne peut être faite pour les propriétés que vous spécifier en inline-css. Dans le cas de ce blog, ça représente près de 10k (non-compressés) supplémentaires chargés à chaque page, alors qu'il aurait été envisageable de les mettre en cache une fois le premier chargement effectué. Il semblerait cependant que la solution promulguée par Google reste plus performante dans l'écrasante majorité des cas.

Quoiqu'il en soit, ce blog a bien d'autres soucis que de chipoter avec les recommandations de Google. Pour gagner encore en performance, une véritable optimisation du code CSS et JS est nécessaire. Quant à vous, lecteurs, j'espère que vous aurez trouvé ici quelques réponses qui vous seront utiles dans vos optimisations web.