Un petit peu d'optimisation de ce site

Un petit peu de travail pour optimiser le chargement de ce site et obtenir le “Saint Gral” : 100/100 au test Google Page Speed Insights, que ce soit sur mobile ou sur navigateur classique.

C’est désormais chose faite. Pour y parvenir, voici les étapes que j’ai franchies.

J’utilise un thème Hugo pré-construit (coder). Comme de nombreux thèmes, celui-ci s’appuie énormément sur les CDN pour servir les ressources habituelles :

  • font-awesome
  • polices chez Google

La partie facile : tout ramener en local et ne plus dépendre des CDN externes

La premère étape a été de tout rappatrier en local pour éviter de faire un trop grand nombre de requêtes sur de nombreux sites différents. Cela peut paraitre contre-intuitif, car l’objet de ces CDN est bien d’éviter à chacun de télécharger toujours les mêmes ressources à de multiples reprises, dans les faits, on n’y gagne pas tant que cela (tout le monde utilise des versions différentes) et surtout on dépend très fortement de ces CDN !

Ainsi, très facile de télécharger en local les CSS concernés et de changer le chemin vers elles dans le template. Mais cette recette n’est pas adaptée pour les ressources plus complexes telles que les polices de caractères (Google Fonts et Font-Awesome), il faut faire quelques petits efforts suppplémentaires.

font-awesome

font-awesome est une combinaison (complexe) de feuilles de style, de javascript, de polices et d’images… Tout ça pour quelques utilisations rares sur ce site.

Sur leur blog on trouve tout de même de quoi faire le travail espéré :

  • il faut télécharger le dossier “free” et le dézipper en local
  • sélectonner ce dont vous avez besoin uniquement, en général :
    • le fichier all.css qui est indipensable
    • le dossier webfonts qui est le minimum que j’utilise. Ce dossier est à placer à la racine de votre site, tout est référencé dans la CSS en mode /webfonts

Une fois cela réalisé, vous servez font-awesome depuis votre site !

Google Fonts

J’ai trouvé l’article How to Self-Host Google Fonts on Your Own Server qui résume parfaitement les étapes à faire pour y parvenir. Pour chaque police dont vous dépendez :

  • rendez-vous sur google-webfonts-helper
  • recherchez votre police
  • sélectionnez les tailles demandées pour votre police. Par exemple, dans mon thème, il y avait la ligne suivante :
    • <link href="https://fonts.googleapis.com/css?family=Lato:400,700%7CMerriweather:300,700%7CSource+Code+Pro:400,700&display=swap" rel="stylesheet">
    • dans cette ligne, on lit pour la police Lato les tailles 400 et 700
  • copiez la CSS proposée (j’ai choisi la version normale non optimisée pour supporter les anciens navigateurs) dans une feuille de style dédiée
  • téléhargez et dézippez la police et placez-là par exemple dans un dossier fonts à la racine de votre site

J’ai donc fait un feuille de style fonts.css et j’y ai ajouté toutes les polices de Google.

Je vous invite également à lire Should you self-host Google Fonts? qui est très complet sur le sujet (je ne l’ai trouvé que plus tard) :

To answer the question in the title of this post: yes it’s better to self-host as the performance gains are substantial.

Il nous reste encore un peu de chemin à parcourir pour obtenir la note parfaite.

Optimiser le chargement de la page

En s’appuyant sur les recommandations proposées par Google Page Speed Insights, on se rend vite compte qu’on peut gagner quelques points facilement :

  • diminuer le nombre de requêtes HTTP
  • diminuer la taille des resources statiques
  • ne pas faire de rendu bloquant

Voyons voir comment je m’y suis pris…

Diminuer le nombre de requêtes HTTP

Le pluis simple pour réaliser cela, c’est de mettre toutes vos CSS dans un seul fichier ; attention cela peut avoir un impact négatif si vous avez de nombreux styles qui bloquent le rendu. Le sujet est vaste, on ne va pas l’aborder ici.

J’ai pour ma part 3 CSS que je voulais charger dans un seul fichier. J’utilise Hugo pour générer ce site, j’ai eu quelques difficultés à comprendre comment fonctionnait la fonction resources.Get.

En effet, comme les CSS sont des fichiers statiques, ils doivent se trouver dans le dossier static et hugo les recopie à la racine du sité généré, tels quels.

Pour combiner plusieurs fichiers statiques, il faut considérer ces fichiers comme des ressources pour que Hugo réalise un traitement dessus. Pour que hugo les voit, ces fichiers doivent être présents dans un dossier assets. Après les avoir déplacés, ils sont accessibles par leur nom avec resources.Get. Ainsi, on charge chaque CSS dans une variable avec resources.Get, ensuite on les combine ensemble avec resources.Concat :

{{- $fontCss := resources.Get "css/fonts.css" }}
{{- $normalizeCss := resources.Get "css/normalize-8.0.1.css" }}
{{- $customCss := resources.Get "css/custom.css" }}
{{- $css := slice $fontCss $normalizeCss $customCss | resources.Concat "css/mytheme.css" }}
<link rel="stylesheet" href="{{ $css.Permalink }}" media="screen"/>

On passe ainsi de 3 requêtes à une seule. On peut faire de même pour les fichiers javascript également.

Diminuer la taille des resources statiques

Hugo permet de minifier les contenus qu’il génère :

  • pour tout le site, à la construction, avec l’option hugo --minify qui va compresser tous les fichiers générés (HTML, json, XML)
  • pour les ressources statiques avec le pipe resources.Minify qu’il suffit d’ajouter à la création de la variable $css : {{- $css := slice $fontCss $normalizeCss $customCss | resources.Concat "css/mytheme.css" | resources.Minify }}

On peut aller plus ploin et en profiter pour faire du fingerprinting et du SRI de nos ressources statiques avec le pipe resources.Fingerprint. Cela nous donne le template suivant :

{{- $fontCss := resources.Get "css/fonts.css" }}
{{- $normalizeCss := resources.Get "css/normalize-8.0.1.css" }}
{{- $customCss := resources.Get "css/custom.css" }}
{{- $css := slice $fontCss $normalizeCss $customCss | resources.Concat "css/mytheme.css" | resources.Minify | resources.Fingerprint "sha512" }}
<link rel="stylesheet" href="{{ $css.Permalink }}" integrity="{{ $css.Data.Integrity }}" crossorigin="anonymous" media="screen"/>

La code généré dans la page HTML est le suivant :

<link rel=stylesheet
href=https://jp.caruana.fr/css/mytheme.min.48d62b9e7e906ff0bfd3b5e106f8d66d8c89924348bd360f2bde5455abdd74c60884498e89d3b75708cd183e9f195f148074ee33375453adb3b3d5b816453564.css
integrity="sha512-SNYrnn6Qb/C/07XhBvjWbYyJkkNIvTYPK95UVavddMYIhEmOidO3VwjNGD6fGV8UgHTuMzdUU62zs9W4FkU1ZA=="
crossorigin=anonymous media=screen>

Eviter les rendus bloquants

Je me suis rendu-compte que le chargement des polices pouvoit bloquer le rendu. L’option CSS font-display: swap permet de dire au navigateur de dessiner les mots sur la page avant la fin du téléchargement de toutes les polices, quitte à changer l’affichage final de manière un peu brutale (on peut observer un effet de clignotement). Pour que cela fonctionne, il faut bien sûr utiliser les bonnes pratiques du web en préciser à chaque fois un famille de police de plus en plus générale, par exemple : text-font-family: Merriweather, Georgia, serif;

 @font-face {
  ...
  font-display: swap;
 }

Enfin, le chargement de font-awesome pesait lourd dans le rendu, j’ai donc choisi de la déplacer en fin de document, hors de la balise <head>, juste avant la balise </body>.

Avec toutes ces mesures, j’ai pu atteindre la note maximale :

Réactions

  • avatar Jean-Philippe Caruana le 2020-08-25 :

    Je viens d’ajouter la recherche sur ce site : elle est disponible depuis le lien /search. Enjoy 😉

    J’ai eu quelques difficultés à la mettre en place : la documentation sur hugo à ce sujet est difficle à comprendre et vraiment pas claire du tout ; poser des questions sur le forum amène souvent à des réponse de la forme “lis la doc” qui n’aident pas du tout au regard de la complexité de celle-ci. Il existe un grand nombre d’articles sur le sujet, mais hugo a évolué tellement que la plupart de ces articles proposent quelque chose qui ne fonctionne pas.

    Je me suis fortement inspiré de ce gist. Contraitement à ce qu’on trouve en général sur internet, cette implémentation s’appuie sur fuse.js et non sur lunr.js comme on trouve habituellement aujourd’hui. Je n’ai pas d’avis sur la question, mais en tout cas cela fonctionne pour moi, c’est la première qualité du code !

    Par contre je suis un peu “chagrin” car il a fallu que j’implémente à nouveau un peu de templating du site pour les réponses à une recherche, mais en js/html plutot que en hugo : de la duplication pas facile à faire sauter… J’ai également fait le choix de ne pas servir les fichiers javascript supplémentaires (fuse, jquery) par mon site mais de passer par les CDN existants (contrairement à ce que j’ai fait dans Un petit peu d’optimisation de ce site) car je me dis que la recherche sera très peu utilisée et qu’il n’est pas nécessaire d’en optimiser le chargement.

    Mais alors, pourquoi l’avoir implémentée cette recherche ?

    1. c’est tout de même fun à faire
    2. je voulais progresser dans ma connaissance de hugo
    3. c’est une étape indispensable pour passer en IndieMark niveau 2 (en fait, je suis plutôt déjà niveau 3 sur cet aspect) qui impose d’avoir un champ de recherche sur son site web.

    J’avance bien sur mon passage en niveau 2 qui est constitué des éléments suivants :

    • ✅ avoir une h-card complète et valide, avec icône de soi
    • ✅ avoir posté plusieurs types de contenus différents (ici des articles de blog et des notes)
    • ❌ faire du POSSE vers les silos existants (par exemple, twitter) avec des boutons d’actions pour y réagir/reposter
    • ✅ avoir une navigation intégrée au site (des liens suivant/précédent entre les articles)
    • ✅ avoir une interface pour rechercher des contenus directement sur son site (pas nécessairement hébergée par le site en niveau 2, mais qui peut le plus peut le moins)

    Il ne me reste plus qu’un point à résoudre pour arriver au niveau 2, le niveau 3 implique de faire toutes ses interractions, y compris avec twitter et autres, depuis son propre site… exitant !