Accélérer vos pages Web : quelques réflexes ⚡️ (partie 1)

Alexandre Cantin
Jul 7 · 11 min read

Cet article sera le premier d’une série de trois :

  1. Images et polices personnalisées (cet article)
  2. JavaScript et pré-chargement de ressources : https://link.medium.com/xxRg7Yc7j8
  3. Contenus animés et LightHouse (à venir)

Depuis sa naissance en 1989, le Web n’a cessé d’offrir plus de possibilités aux développeurs afin de créer la meilleure expérience possible pour nos utilisateurs : images, vidéos, interfaces dynamiques avec JavaScript, polices personnalisées etc.
Toutefois, cela n’est pas sans conséquence : le poids de nos pages a augmenté et il n’est pas rare qu’une page Web dépasse désormais un voire plusieurs mégaoctets.

D’ailleurs, selon [Httparchive.org](https://httparchive.org/), en dix ans, malgré l’augmentation constante des débits Internet, la vitesse de chargement des pages est restée… la même. De plus, Google considère désormais les performances comme un facteur influençant son algorithme de _ranking_.

Dans cet article, nous présenterons ainsi différentes méthodes pour alléger vos pages. Cette liste sera non exhaustive et nous nous concentrerons sur les principaux leviers parmi la centaine (voire les milliers!) envisageables.


Note : les éléments présentés ci-dessous visent les utilisateurs arrivant pour la première fois sur votre site. Par conséquent, les services workers ne seront pas abordés.
De même, je n’aborderai pas des techniques que je considère comme déjà acquises comme la minification du CSS et du JavaScript

🏞 1 - Les images

1.1 - Réduction du poids : PNG et JPG
Omniprésentes, les images sont les principaux responsables de l’alourdissement du Web. Leur bonne gestion doit donc être l’une de vos priorités et j’oserai même dire un réflexe indispensable en tant que développeur.

Pour ma part, je ne peux que vous conseillez d’utiliser https://tinypng.com/ qui fonctionne via un simple drag and drop.

Nous allons maintenant tester son efficacité avec l’image de ce chat au format JPEG et pesant 357 Ko sans optimisation :

Si nous l’optimisons, TinyPng nous indique que le poids du fichier a été réduit de 49%, de 357 Ko à 181 Ko :

Un poids divisé de moitié pour un rendu équivalent :

Sachant que ce gain se répète pour chacune image de votre page, on comprend vite l’intérêt crucial de bien optimiser l’ensemble de ses images.

1.2 - Quand choisir JPEG et PNG ?

Un autre point important est de choisir le bon format en fonction de vos besoins. En effet, ils répondent tous les deux à des exigences différentes et votre choix dépendra de plusieurs facteurs :

  • la taille de l’image : pour les “grandes” images (photos notamment), il faudra privilégier le format JPEG. Pour les “petites” images (émoticônes, icônes…), il faudra opter pour les PNG. Et si votre image est de taille “moyenne” ? Il faudra mener votre propre enquête pour déterminer le meilleur choix (en fonction du poids mais aussi des autres critères).
  • précision du dessin : le format JPEG, étant un format compressé, les contours perdent en netteté et cela n’est pas forcément souhaitable (graphiques, logo etc.). Il faudra opter pour le format PNG dans ce cas.
  • transparence : dans ce cas-là, le PNG est la seule solution 🙂.

1.3 - Réduction du poids : SVG

Les images SVG peuvent aussi être optimisées. Pour ma part, j’utilise SVGOMG qui fonctionne lui aussi via drag and drop.

Dans l’exemple ci-dessus, on constate que le poids de notre SVG a réduit de 60%, passant de 1.5 Ko à 643 bytes et, même si le résultat reste moins spectaculaire comparé aux PNG et JPG, cela reste toutefois appréciable 🙂.

1.4 - Allez plus loin avec Webp

Créé par Google en 2010, le Webp est un format d’image qui se veut - en moyenne - 30% plus léger comparé au JPEG et 25% par rapport au PNG; tout en conservant gardant les effets de transparence pour ce dernier format.

Le Webp est donc un remplaçant de choix pour tous vos fichiers JPG et au niveau des PNG, Webp étant un format compressé, cela dépendra principalement de la précision du contour que vous souhaitez obtenir.

Un des soucis majeur de ce format est son support dans les principaux navigateurs. En effet, bien que Chrome, Firefox et Edge (uniquement dans sa dernière version) acceptent ce format, Safari le supportera dans sa prochaine version :

Cela ne doit pas pour autant vous décourager d’utiliser le format Webp et nous verrons plus tard comme gérer les navigateurs non compatibles.

Pour convertir vos images en Webp, je peux vous conseiller d’utiliser XnConvert qui fonctionne directement sur votre ordinateur (disponible pour Windows/Linux/MacOs). Comme solutions en ligne, https://ezgif.com/png-to-webp et https://ezgif.com/jpg-to-webp fonctionnent très bien 🙂.

La conversion se réalise en 3 étapes (nous partirons de l’image du chat optimisée précédemment) :

  1. Ajouter les images :

2. Cliquer sur l’onglet “Sortie” puis :

  • Indiquer “{Filename}.png” ou “{Filename}.jpeg” dans le champ “Nom du fichier”. Le choix de ce formatage sera important pour la suite 😉
  • Choisir “WEBP-Webp” dans le bloc format
  • Décocher la case “Préserver les métadonnées” et cocher “Préserver l’extension”

3. Pour finir, cliquer sur “Convertir” en bas à droite de la fenêtre :

Nous obtenons ainsi une image avec le nom : [nom-image].jpeg.webp (ou [nom-image].png.webp). Dans notre exemple, le poids est passé de 181 Ko à 149 Ko.

1.4.1 - Cohabitation entre Webp et PNG/JPEG avec

Introduite en 2015, la balise permet de spécifier plusieurs règles afin que le navigateur puisse déterminer l'image la plus pertinente à télécharger. Plusieurs conditions sont possibles : densité de pixels, dimensions du navigateur... et le format d'image 🙂.

Pour utiliser , il suffit de lui associer plusieurs :

<picture>
<source srcset="chat-mignon.webp" type="image/webp">
<source srcset="chat-mignon.jpeg" type="image/jpeg">
<img src="chat-mignon.jpeg" alt="" />
</picture>

Dans cet exemple, le navigateur optera pour le format Webp s’il le gère et JPEG dans le cas contraire. L’usage de est donc d'une grande simplicité 🙂.

Malheureusement, il nous reste le cas d’une image importée via CSS et malheureusement, il n’existe pas d’équivalents à pour le CSS.

La solution est alors d’utiliser JavaScript pour déterminer ce support et notamment via ce snippet (proposé par Jake Archibald) :

async function supportsWebp() {
if (!self.createImageBitmap) return false;

const webpData = 'data:image/webp;base64,UklGRh4AAABXRUJQVlA4TBEAAAAvAAAAAAfQ//73v/+BiOh/AAA=';
const blob = await fetch(webpData).then(r => r.blob());
return createImageBitmap(blob).then(() => true, () => false);
}

En fonction de la réponse, vous pourrez ajouter une classe ou à la balise pour, ensuite, au niveau du CSS, utiliser cette classe pour appeler la bonne image :

.no-webp .cat-background {
background-image: url("chat-mignon.jpg");
}

.webp .cat-background {
background-image: url("chat-mignon.webp");
}

1.4.2 - Cohabitation entre Webp et PNG/JPEG avec nginx

Si vous ne souhaitez pas convertir l’ensemble de vos balises de votre projet ou d'utiliser une classe à la balise , il est possible de délivrer le bon format d'image en fonction du support du navigateur et cela grâce à Nginx.

Pour obtenir ce comportement, la premier prérequis sera de faire cohabiter l’image JPEG/PNG à côté de son équivalent Webp. Nous obtenons ainsi :

images/
- chat.jpeg
- chat.jpeg.webp

Dans un second temps, nous utiliserons l’instruction de Nginx pour créer une variable . Cette dernière contiendra ou sera vide en fonction du support de Webp par le navigateur. Ce support sera déterminé suivant le contenu du header HTTP de la requête :

# Detect if browser accepts .webp images ?
map $http_accept $webp_suffix {
default"";
"~*webp" ".webp";
}

Pour finir, nous utiliserons la commande de Nginx, permettant de tester la présence de plusieurs fichiers successivement et renvoyer le premier trouvé.

# Handle .webp

location ~* ^/assets/.+\.(jpe?g|png)$ {
root /home/deploy/build/;
try_files $uri$webp_suffix $uri =404;
}

Ainsi, nous testerons dans l’ordre :
1 — l’URL de la ressource concaténée avec la valeur pouvant donc être ou
2 - l'URL demandée en solution de secours (en cas d'absence du fichier Webp notamment)
3 - Erreur 404

Nous obtenons un système faisant cohabiter à la fois les images au format JPEG/PNG et Webp, basée sur la prise en charge du format par le navigateur de l’utilisateur.

D’ailleurs, on constate sur la console de Google Chrome que le fichier reçu est bien une image Webp :

1.5 - Lazy-loading

Les images les plus légères restent celles qu’on ne télécharge pas. Pour cela, le lazy-loading est la solution toute nommée et consiste à télécharger une ressource seulement si cela s’avère nécessaire. Dans le cas d’une image, l’élément déclenchant son téléchargement sera ainsi son apparition — ou sa proche apparition — dans la zone d’affichage de la page Web.

1.5.1 - Avec Lazysizes

Lazysizes est une librairie JavaScript nous facilitant la mise en place du lazy loading d’images. D’une taille compressée de 3.2Ko, son utilisation se veut assez simple en renommant notre attribut en :

<img data-src="static/logo.jpg" class="lazyload" alt="" />

De plus, Lazysize nous permet aussi d’adapter les images en fonction de l’appareil de l’utilisateur (mobile, tablette ou PC).

<img
alt=""
class="lazyload"
data-srcset="small.jpg 500w, medium.jpg 640w, big.jpg 1024w"
data-src="medium.jpg" />

Simple mais terriblement efficace 🙂.

1.5.2 - Avec l’attribut de

La balise accueille depuis peu un nouvel attribut qui, couplé avec la valeur , permet d'implémenter nativement un système de lazy loading :

<img src="chat-mignon.png" loading="lazy" alt="" />

Sa prise en compte est donc loin d’être garantie… Toutefois, vous ne perdez rien à implémenter ce nouvel attribut qui sera tout simplement ignoré par les navigateurs non compatibles 🙂 (qui téléchargeront donc directement l’image).


🚨 2 - Les polices personnalisées

Relativement récent dans l’histoire du Web, l’ajout des polices personnalisées apporte une touche de personnalisation supplémentaire au design de nos pages. Et même s’elles sont minimes, des optimisations restent possibles.

2.1 - Fonctionnement des polices personnalisées

Avant de nous attarder sur les améliorations de performances possibles, il est nécessaire de présenter le fonctionnement des polices personnalisées.

Tout d’abord, l’inclusion d’une police peut se réaliser de plusieurs manières mais les deux méthodes les plus courantes sont :

1- L’import direct de la police depuis notre serveur

@font-face {
font-family: "Open Sans";
src: url("/fonts/OpenSans-Regular-webfont.woff2") format("woff2"),
url("/fonts/OpenSans-Regular-webfont.woff") format("woff");
}

2- Via une ressource externe (ici, Google Fonts) :

<link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@300" rel="stylesheet">

Dans tous les cas, l’utilisation dans nos styles CSS est identique :

body {
font-family: "Open Sans", serif;
}

Bien entendu, la police n’est pas directement présente sur le navigateur et doit donc être téléchargée. Mais que se passe-t-il pendant ce temps au niveau de l’affichage ? Figurez-vous que le navigateur n’affiche tout simplement…rien ! Plus exactement, il compense l’absence de la police en la remplaçant par une police invisible dont la taille se veut proche du résultat final (via font-size et font-weight principalement).

Par défaut, cette situation va durer 3 secondes (sur Firefox et Chrome tout du moins) avant que le navigateur bascule sur la seconde police (serif dans notre exemple). Trois secondes pendant lesquelles l’utilisateur ne pourra lire le contenu de votre site…

Bien sûr, dès que la police sera téléchargée, l’affichage de votre page sera mis à jour (que ce soit avant ou après les 3 secondes de délai).

2.2 - Utilisation de display: swap

La règle CSS permet de modifier la politique d'import des polices personnalisées, sa valeur par défaut est et correspond au scénario décrit précédemment.

En remplaçant par , le délai de basculement vers la police de remplacement passe alors de 3 secondes à 100 ms ! Un gain de temps appréciable et réduisant ainsi le Time to first meaningful paint, métrique définie par Google correspondant au temps d'apparition du premier contenu pertinent.

Son utilisation est extrêmement simple.

Dans notre premier cas d’import, nous ajoutons simplement dans le bloc :

@font-face {
font-family: "Open Sans";
src: url("/fonts/OpenSans-Regular-webfont.woff2") format("woff2"),
url("/fonts/OpenSans-Regular-webfont.woff") format("woff");
font-display: swap;
}

Pour Google Fonts, depuis mai 2019, il suffit d’ajouter ‘&display=swap’ dans l’URL d’import pour obtenir, par exemple :

<link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@300&display=swap" rel="stylesheet">

Si vous souhaitez gérer plus finement votre police d’import de polices, autorise d'autres valeurs :

  • font-display: block
    Cette option est semblable au fonctionnement par défaut : 3 secondes de police invisible avant de basculer sur la police de remplacement. Son seul intérêt est de s’assurer que nous ayons bien ces 3 secondes de délai, étant donné que tous les navigateurs peuvent avoir une politique différente comparée à celle de Firefox et Chrome.
  • font-display: fallback
    L’option réduit aussi le délai de basculement vers la police de remplacement à 100 ms mais limite aussi la durée de récupération de la police personnalisée à 3 secondes (là où n'impose pas de durée de temps). Elle permet donc d'indiquer au navigateur d'arrêter la récupération de la police si son téléchargement s'avère trop long.
  • font-display: optional
    Plus radicale que , elle réduit le délai de basculement à 100 ms mais arrête la récupération de la police par la même occasion. Traduction pour le navigateur: "Si tu as la police sous la main (donc en cache) tu l'utilises, sinon inutile de la récupérer".

Fin de la première partie

Nous voici arrivés à la fin de cette première partie consacrée aux images et aux polices personnalisées.
Dans le prochain épisode, nous aborderons les optimisations liées JavaScript et le pré-chargement de ressources.

Restez connecté 🙂.

Restez connecté 🙂.


🔰 Aller plus loin

Vidéos et articles

Voici un ensemble de ressources pour explorer l’univers de l’optimisation des chargements :

Sur Twitter

CodeShake

Learnings and insights from SFEIR community.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch

Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore

Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store