• Ce blog — désormais archivé — est en lecture seule.

Optimiser les performances de son site web ? Pariez sur le frontend !

Il y a quelques jours je retraçais mes premiers pas dans l’optimisation de mon site dans cet article /combo-handler-optimiser-le-nombre-de-requetes-http-pour-les-fichiers-css-et-javascript/, reprenant essentiellement les best practices Yahoo. Les optimisations ont lieu sur la partie frontend d’un site, c’est-à-dire tout ce qui se charge derrière l’exécution du code (PHP en l’occurence) et que l’on nomme backend.

Je me suis documenté sur le sujet et j’ai découvert d’autres techniques. Des techniques qui poussent l’optimisation plus finement, d’autres qui nécessitent de se poser les bonnes questions.

J’évoquerai ici les techniques découvertes et mises en place sur ce site.

I – Commencer avec les bonnes pratiques Yslow

C’est depuis cette présentation d’Eric Daspet que je m’intéresse de près à l’optimisation de la partie frontale d’un site web. Implicitement je respectais certaines bonnes pratiques Yahoo mais cette présentation met l’accent sur d’autres possibilités qui rendent l’optimisation des plus efficaces.

14 catégories pour les bonnes pratiques Yslow, un peu plus pour Google et son Google Page Test. Au final, il faut retenir :

  • Les fichiers CSS minimisés, compressés en haut de page
  • Les fichiers JavaScript minimisés, compressés et en bas de page
  • Les images, flash, CSS, JS et tous les médias statiques de manière générale doivent être compressés (gzip/deflate) et en cache.
  • Diminuer le nombre de requêtes HTTP, pour cela : réduction du nombre de fichiers CSS, JavaScript et images.
  • Bien sûr ne pas utiliser les filtres alphaloader (en CSS) qui permettent de gérer de la transparence sous IE.
  • Enlever le contenu dupliqué pour les CSS et JavaScript.
  • Optimiser les images
  • Mettre du cache sur les requêtes AJAX. Point souvent oublié ou inconnu et pourtant depuis le temps que le web2.0 existe…

Avec Yslow, on obtient une note sur 100 et un grade de A à F (A étant notre objectif). Le mieux est d’avoir 100/100 mais certaines règles ne peuvent pas toujours être satisfaites faute de moyen technique (CDN, Sous-domaines). Google met l’accent sur l’optimisation des médias en tout genre. Il va encore plus loin.

Ces techniques ont fait diminuer mon temps d’accès à la page index de près de la moitié du temps initial. Mais on peut faire encore mieux.

II - Plus loin avec le Combo Handler

Déjà évoqué dans l’article précédent, je remet le code du combo handler en version un peu plus optimisé. C’est presque rien mais cela m’a fait gagner une centaine de millisecondes (cf. la vidéo ou la présentation) :

<?php

  ob_start('ob_gzhandler');

  header('Cache-Control: public;max-age=315360000');
  header('Expires: Thu, 15 Apr 2010 20:00:00 GMT');
  header('Content-Type: text/javascript');

  if(!empty($_GET['files']))
  {
    foreach(explode('|', $_GET['files']) as $js)
    {
      if(substr($js, -3) == '.js')
      {
        readfile('../js/' . $js);
        echo "\n";
      }
    }
  }

?>

Ce Combo Handler a cette configuration car mes pages utilisent différents CSS selon la page qui s’affiche et ces fichiers sont déterminés lors de la construction de la page (dans le cheminement de mon MVC).

L’intérêt ici est de diminuer le nombre de requêtes HTTP. Avec cette solution j’ai gagné environ 15 requêtes HTTP en moins. Dans le schéma ci-dessous on peut voir le nombre de requêtes effectuées lors du premier appel de la page : 22 requêtes dont une pour le CSS et deux pour le JavaScript (dont un appel externe, nous verrons cela plus loin). Or j’ai pas moins d’un dizaine de CSS et un peu moins pour les JavaScript.

On peut également voir le cache en action, lorsqu’on a chargé une fois la page, les autres fois nous n’aurons à attendre que le temps d’exécution du code et le rendu HTML. C’est très appréciable.

Seulement pour obtenir ce graphique, j’ai utilisé d’autres techniques comme le sprite CSS.

III - Sprite CSS et images

La technique du sprite CSS consiste à rassembler plusieurs images en une afin de diminuer le nombre de requêtes et diminuer le temps de chargement de ces images. Le plus souvent, la somme des temps de chargement de chaque image est supérieure au temps de chargement de l’image contenant toutes les images.

Voilà mon sprite CSS :

Ce sprite me permet en plus de faire du pré chargement d’images (les images des liens sociaux utilisées dans le détail d’un article par exemple). Gain de temps encore et toujours.

Deux sites proposent la création d’un sprite à partir d’une liste d’images et la création du CSS correspondant : CSS Sprites generator et http://spritegenwe.bsite-performance.org/.

Après ces optimisations je me suis retrouvé confronté à un problème avec les médias externes : le statut de mon twitter (JSON) et les deux images FeedBurner. J’ai réglé ce problème en avec un peu de JavaScript.

IV – Vous avez dit JavaScript ?

Le JavaScript est bloquant. Il est important de bien s’en occuper pour gagner en performance. Pour ma part, je charge le combo JavaScript en toute fin de page puis je charge le reste du JavaScript sur l’événement onload de la page.

J’ai fait ce découpage car charger tout le JavaScript sur l’événement onload ne fonctionnait pas. Il faudrait, je pense, utiliser des timers et je n’en veux pas : timer = temps d’attente.

Pour les médias externes qui bloquent généralement à cause du temps de latence entre les serveurs, j’ai choisi de les charger en JavaScript comme ceci :

// Twitter Status
var dynamic_script_3 = document.createElement("script");
dynamic_script_3.src = "http://twitter.com/statuses/user_timeline/couac.json?callback=twitterCallback2&count=1";
dynamic_script_3.type = "text/javascript";
document.getElementsByTagName("head")[0].appendChild(dynamic_script_3);

Ce script est appelé dans une fonction affectée à l’événement onload de la page comme suit :

function afterRun() {
// ...
// Twitter status
...
}

if(window.addEventListener)
  window.addEventListener("load", afterRun, false);
else if (window.attachEvent)
  window.attachEvent("onload", afterRun);
else window.onload = afterRun;

Ainsi l’appel JavaScript se fait une fois la page totalement chargée, ce qui n’est pas bloquant. J’ai fait la même chose pour les deux images FeedBurner.

Maintenant que la page n’est plus bloquée, que les optimisations sur les médias ont été faites, que le cache et la compression sont en place, que peut-on faire ?

V – D’autres optimisations

J’ai effectué d’autres optimisations sur le code métier et sur la structure HTML, les voici.

PHP & Flush
<?php flush(); ?>

J’utilise la fonction PHP flush() pour envoyer l’entête de ma réponse HTML c’est-à-dire depuis la balise ouvrante HTML jusqu’à la balise fermante HEAD. Le gain est significatif dans mon cas. Découper plus ne ma rien apporté par contre.

Noeuds DOM et structure HTML

Du côté HTML, il faut avoir le moins de noeuds DOM possible :

alert(document.getElementsByTagName('*').length);

Ceci affichera dans une alerte le nombre de noeuds sur votre page. Pour ma part j’ai 562 noeuds sur l’index, Yahoo en a 700 et il n’y a pas de moyenne précise. Pour moi, il devrait être bon de ne pas en avoir plus de 1000 mais tout dépend ce que l’on doit afficher.

Reflow et navigateur

Autre optimisation, éviter le reflow : c’est-à-dire éviter de redéclencher le processus de rendu du navigateur. Pour cela :

  • faire deux boucles si une boucle effectue deux traitements
  • proposer des structures fixes au navigateur
  • éviter les tableaux à largeur de colonne variable
  • éviter les tableaux imbriqués
  • définir à l’avance les tailles des images

Comme on peut le voir, ce sont des points visant à améliorer les performance du navigateur.

Une autre forme d’optimisation sur les fichiers CSS et JS

Si on a beaucoup de CSS et JavaScript mais que les fichiers restent statiques (à la nouvelle mise en production près) on peut tenter une optimisation côté serveur. On exécute un script qui regroupe, minimise, compresse et met en cache nos fichiers CSS et de même pour nos JavaScript. Ainsi on ne servira qu’un seul fichier de chaque et on s’affranchit de l’exécution à la volée du script PHP comme je le fais.

Parallélisation

On peut aussi paralléliser le chargement de sa page. La ligne de conduite en vigueur est qu’un navigateur peut effectuer deux requêtes en parallèle vers un même domaine ou sous-domaine.

En multipliant les domaines ou sous-domaines on réduit les temps de chargement. Une technique simple est de créer un sous-domaine media et d’utiliser ce sous-domaine pour effectuer toutes les requêtes HTTP vers les images, CSS et JavaScript.

Sprite CSS vs Parallélisation

Faut-il utiliser un sprite si l’on a 6 images à charger ou 3 sous-domaines (en admettant les avoir) ?

C’est à tester selon la configuration matérielle. Personnellement, j’utilise le sprite pour effectuer du pré chargement en plus du chargement utile. S’il n’y a pas de pré chargement à faire, le parallélisme pourrait être un peu plus rapide mais sans garantie.

Un frontend épuré

Certains sites à fort trafic adoptent cette éthique. Il s’agit de faire un design très épuré, sans images ou très peu, avec un CSS basique et un découpage simple. Ce sont des idées dont il faut tenir compte mais l’internaute sera toujours attiré par quelque chose de joli.

Toujours plus ?

J’ai trouvé cette présentation très complète. Elle retrace ce qui a été dit ci-dessus et va encore plus loin. Elle introduit une nouvelle notion : le web sur les mobiles.

VI - Conclusions

En chiffres ?

Ces techniques m’ont permis de supprimer près de 20 requêtes HTTP, de passer ma page à 200ko sans cache et moins de 10ko avec, de gagner plus de 2s sur le chargement d’une page et d’obtenir 93/100 au test Yslow.

En temps pour tout mettre en place ?

Infime (quelques soirées), le plus long a été le temps passé pour se documenter. Les optimisations basiques type Yslow se font rapidement et sont très vite significatives.

En intérêt ?

Un site plus réactif aura de meilleurs chances d’avoir plus de visiteurs. Pas parce qu’il sera mieux, simplement parce que lorsque les visiteurs tomberont dessus, ils n’auront pas à attendre. Ces optimisations ne feront pas augmenter la réputation de votre site.

Pour l’avenir ?

« Le web a changé » dit Eric Daspet. Aujourd’hui il y a tellement de sites identiques qu’il faut se démarquer. L’optimisation des performances est un moyen efficace pour faire la différence.

Mon site ?

Ce qui bloque sur mon site, c’est le code PHP pas du tout optimisé, imaginez un code PHP qui prendrait tout au plus 700ms. Mon site s’afficherait en quasi instantané, plutôt convaincant !

L’intérêt sur un site comme le mien ? A première vue aucun, nous sommes d’accord, mais c’est le seul moyen que j’ai trouvé pour étayer mon discours en entreprise.

Je résume ici ma vision des choses au fur et à mesure que j’apprend. Eric Daspet (Yahoo) et son homologue americain Steve Souders (Google) sont mes principaux inspirateurs grâce à leurs multiples interventions et articles. Je suis en constante réflexion sur ce sujet et je tente de découvrir les meilleurs moyens d’obtenir des gains significatifs de performance.

  • Print
  • Digg
  • StumbleUpon
  • del.icio.us
  • Facebook
  • Twitter
  • Google Bookmarks
  • FriendFeed
  • LinkedIn
  • MySpace
  • Netvibes
  • PDF
  • Ping.fm
  • RSS
  • Technorati
  • viadeo FR
  • Wikio
  • Yahoo! Buzz

Related Posts

Cet article a été publié dans Ancien blog avec les mots-clefs : , , , , . Bookmarker le permalien. Les commentaires et les trackbacks sont fermés.

Un trackback

  1. [...] cela j’ai trouvé un script JS qui n’exécute une fonction qu’une fois le chargement de la page entièrement [...]