Les Performances web, pour aller plus loin

  • Profil : Expert
  • Technologie : DOM
  • Thème : Qualité
  • Auteur : Nicolas Hoffmann
  • Mise à jour : 04/07/2011

En bref

Dans Introduction à la performance web, les bases des bonnes pratiques et des enjeux de la performance web ont été posées. Cet article a pour but d’aller plus loin dans ce vaste domaine en décryptant l’affichage d’une page et les pistes pour l’accélérer.


Principe du chargement d’une page

Le graphique en cascade (waterfall en anglais) permet de mieux comprendre comment une page s’affiche. Voyons un exemple réalisé avec Web Page Test qui, dans ce cas, simule un chargement d’une page :

Affichage d'une page avec Web page test

Le vocabulaire

Posons les bases du vocabulaire : ce graphique en cascade indique le chargement d’une page. Cet exemple est un premier accès (First view) : il faut tout télécharger, nous ne bénéficions pas de la mise en cache des contenus. Nous pourrons voir plus loin dans cet article les avantages cette mise en cache dans un second accès (Repeat view).

La requête DNS (DNS Lookup) permet de connaître l’adresse IP correspondant au nom de domaine demandé, ce qui permettra de s’y connecter.

La connexion initiale (Initial Connection) indique le temps pour effectuer la connexion TCP/IP afin de lancer le téléchargement de chaque fichier.

Le début de rendu (Start Render) indique le moment où le navigateur commence à effectuer son travail de rendu, après avoir téléchargé les fichiers CSS (symbolisé par la barre verticale verte sur l’image ci-dessus).

La barre bleue indique quand le document est complet (Document Complete), c’est-à-dire quand les éléments demandés pour le rendu de la page ont tous été téléchargés et rendus.

Une nuance importante : nous pourrions penser que le document est à la fin du téléchargement des éléments. Il n’en est rien : le document est considéré comme totalement chargé (fully-loaded) peu après la barre du document complet. Dans notre exemple, l’icône de favori se charge après que la page ait été rendue. Nous y reviendrons.

Le nombre d’éléments dans le DOM indique également la complexité de la page. Un DOM conséquent (supérieur à 1000 éléments) sera plus long à afficher par les navigateurs, particulièrement les moins récents comme Internet Explorer 6.

Premières observations

Même si dans notre exemple il n’y a qu’un nom de domaine, il ne peut rien se passer sur un nom de domaine avant qu’il ne soit « résolu » (en vert foncé sur l’image) : cela peut handicaper fortement le rendu d’une page si ces derniers se sur-multiplient. Il est donc aisé de comprendre pourquoi par exemple Google Page Speed recommande de minimiser les requêtes DNS à résoudre.

Néanmoins relativisons ce propos : la ligne de conduite généralement admise est la suivante : un navigateur peut effectuer six requêtes en parallèle vers un même domaine ou sous-domaine (comme par exemple Firefox 4 et Internet Explorer 9). Internet Explorer 6 et 7 par contre ne peuvent effectuer que deux requêtes simultanées vers un domaine.

A bon escient, cela peut être utilisé pour paralléliser les chargements : 2 domaines = 4 requêtes simultanées pour Internet Explorer 7.

Toujours dans l’exemple, le début de rendu de la page n’intervient pas avant le chargement des CSS et du Javascript placés dans le head… Tout comme la requête DNS bloque le téléchargement des fichiers sur un domaine, le rendu d’une page ne démarre pas avant que les éléments placés dans le head. On comprend aisément pourquoi il est conseillé de minimiser le nombre de CSS et de fichiers Javascript, et pour ces derniers de les placer en bas de page.

Comme notre exemple simule un chargement d’un site en HTML5 sous Internet Explorer, le Javascript est obligatoire dans le head.

Même si l’exemple ne comporte que très peu d’images (une dizaine) sur une page plutôt légère (3 ko), on peut constater que la partie pure du rendu des contenus par le navigateur est déjà plus longue que le temps écoulé avant le début du rendu. Sur des pages plus lourdes, plus complexes et avec plus de fichiers, cette partie augmente drastiquement. C’est pour cela que, souvent par abus de langage, on parle d’optimisation de performances web, on parle d’optimiser l’affichage Front-end (côté navigateur), ce dernier prenant en général le temps le plus conséquent.

Mise en cache avancée

L’exemple de mise en cache des contenus donné dans l’« Introduction à la performance web » était volontairement simplifié à l’extrême. Voyons un exemple plus avancé de mise en cache de contenus dits « statiques » (qui ne sont pas appelés à évoluer, images, CSS, etc.) via un fichier htaccess :

<FilesMatch "\.(js|css|gif|jpg|jpeg|png|ico)$">
Header unset Cookie
Header unset Set-Cookie

Header set Cache-Control "max-age=31536000"

header set vary  "Accept-Encoding"
header append vary "User-Agent"
header append Cache-Control "public" 

header append Connection "Keep-Alive"
header append Keep-Alive "timeout=5, max=100"

FileETag None
</FilesMatch>

Header unset Cookie et Header unset Set-Cookie permettent d’éviter la gestion des cookies pour ces fichiers statiques (qui peuvent amener des requêtes HTTP inutiles).

Header set Cache-Control "max-age=31536000" gère le contrôle du cache : les éléments sont mis en cache pour une année (le nombre correspond à une année en secondes), c’est-à-dire que le cache est valide une année durant.

header set vary "Accept-Encoding" : Cette instruction indique aux proxies de mettre en cache deux versions de la ressource : une compressée, et une non compressée. Ainsi, cela donne la possibilité aux proxies de proposer les deux versions, selon ce que l’utilisateur demande.

Les deux lignes concernant le Keep-Alive permettent d’activer les connexions persistantes, c’est-à-dire de laisser la connexion ouverte au cas où le navigateur ait d’autres requêtes (ce qui économise des requêtes TCP/IP). Attention, si cela améliore les performances pour le client, cela peut faire consommer des ressources et surcharger le serveur… Et donc impacter les performances. En général, il est recommandé de régler cette option sur un serveur de fichiers statiques.

FileETag None : nous n’utilisons pas les Etags, la mise en cache est correctement assurée par le Cache-Control.

Observons le résultat sur la même page sur une seconde consultation :

Second affichage d'une page avec Web page test

Comme nous pouvons le voir, aucun fichier précédemment mis en cache n’est retéléchargé. En résulte une économie conséquente de requêtes HTTP et de bande passante, il ne reste que la page PHP en question. Cette dernière est chargée en 304 millisecondes, et le rendu total passe de 2 secondes à 700 millisecondes (!).

D’autres pistes plus avancées

Lazy-loading

Nous l’avons vu, une bonne utilisation de la mise en cache des contenus permet de bien améliorer la performance lors d’une seconde consultation. Toutefois, il est également possible d’améliorer la première consultation en différant le chargement de certains éléments après que le document soit complet. Des sites comme Twitter ou Gmail utilisent beaucoup cette technique.

On appelle cette technique le lazy-loading (littéralement « chargement fainéant », qu’on traduira plutôt par chargement différé, ou chargement à la demande). Cela se fait via Javascript.

Exemple avec Jquery :

$(document).ready(function(){
 // ici le chargement de vos fichiers 
 // une fois la page chargée et rendue
 // exemple très simple
 $('#image_to_load').attr('src','img.gif');
 });

Ainsi, l’image ayant l’id image_to_load aura sa source chargée après le rendu de la page.

Attention : ce code est donné juste à titre d’exemple pour expliquer le principe de la méthode, il vaut mieux éviter de laisser une balise img vide.

On peut ainsi ne charger :

Tout cela permet de réduire le temps de chargement de la page. Observons le chargement d’une page avec un chargement différé d’une image :

Affichage d'une page avec un élément différé, avec Web page test

Web page test le confirme : l’image a bien été chargée après que le document soit rendu.

Attention : certes le gain de performances peut être important, mais ne sacrifiez pas tout aux performances. Prévoyez une solution de repli au cas où Javascript soit désactivé, sinon il pourrait y avoir perte d’information et/ou de référencement si cette technique est utilisée à mauvais escient ! Dans notre exemple, cela peut se faire via la balise noscript qui indiquera au navigateur n’ayant pas Javascript activé de charger l’image, ou via une redirection vers une version n’utilisant pas de chargement différé.

Diminution du nombre de requêtes HTTP via DATA-URL

Les Data-URL permettent de mettre du contenu directement en ligne dans vos CSS ou dans votre HTML.

Voici un exemple dans une CSS :

background:#000 url(pattern_136.gif) repeat; sera remplacé par : background:#000 url(data:image/gif;base64,R0lGODlhOAA4AIAAABUVFQAAACH5 BAAAAAAALAAAAAA4AdgAAAK/TGB5asiuYJKNrWjpxS7zoXTJ 1ZgaW3juaLay5ou2dbyjas5HOv83PvZhDtaMUbUKZ NepnH51ESnQaoNWs2WnNpr0+oF47jA8lCMNY/R5HBKnXar20c4hR6vn7v6vo8NiBRoZ4Rn+DQo p7jGl+d4l+j31XgoSBn5d6m5hVn5JtlZGLoJStrnu ZiHWspp+ijqupoiu0obaWuKS7jLu6ibChmLOdrLW PxqeVxLDCzs3Py8pzwczSoNPZ2tbc2dVgAAOw==) repeat;

(des retours à la lignes ont été ajoutés pour de simples raisons de lisibilité)

En général, le code produit est un petit peu plus lourd que l’image originale sous forme de fichier. Toutefois, cela économise une requête HTTP. Pensez à bien activer la compression GZIP ou DEFLATE pour optimiser cet équivalent textuel d’une image.

La mise en cache de ces contenus est fonction d’où ils ont été insérés : si, comme dans l’exemple, l’image est dans une CSS mise en cache, elle sera donc de facto mise en cache (en tout cas, le code correspondant). Si par contre, le contenu se situe dans une page HTML ou PHP qui n’est pas mise en cache, le code correspondant ne le sera donc pas non plus.

Attention : Les navigateurs récents comme Firefox ou Opera supportent très bien cette possibilité. Quant à Internet Explorer, il ne supporte cela qu’à partir de la version 8… Et ce dernier limite la taille de la Data-URL à 32 ko. On réservera donc cette technique aux très petites images et de préférence décoratives et non utiles à la compréhension de la page. Encore une fois il ne faut pas tout sacrifier aux performances.

Commentaires conditionnels

Les commentaires conditionnels permettent d’appliquer des règles CSS spécifiques pour certaines versions d’Internet Explorer, et ce afin de patcher son moteur de rendu parfois défaillant ou illogique.

<!--[if lte IE 7]>
    <link type="text/css" rel="stylesheet"
          href="fixes.css" />
  <![endif]-->

Il a toutefois été constaté que ces commentaires conditionnels bloquaient pendant un certain temps le téléchargement des fichiers.

Idéalement, si les performances web sont critiques pour votre projet et si vous pouvez vous passer des commentaires conditionnels, évitez de les utiliser. Toutefois une solution curieuse, mais parfaitement fonctionnelle, résout ce problème : il suffit de mettre un commentaire conditionnel vide en haut de votre source. Exemple :

<html>
<!--[if IE ]><![endif]-->

CONCLUSION

Une meilleure compréhension du mode de chargement et de construction des pages permet d’en optimiser la vitesse de chargement. Une mise en cache soignée ainsi que des chargements différés peuvent accélérer le rendu de vos pages très efficacement.

Il va de soi que cet article n’est pas exhaustif, d’autres techniques sont possibles, elles feront l’objet de probables futurs articles.

Références et outils :


Une question, une remarque ? Écrivez à l'auteur.

6 Messages de forum

Répondre à cet article

Page valide XHTML 1 Strict, CSS2 et accessible AA.
Ce site s'affiche mieux dans un navigateur conforme aux standards, voici pourquoi.
Site hébergé par yterium (depuis 2010) et par l'APINC (2002-2010) . Propulsé par SPIP.