Geek #17

Que se passerait-il si on mettait ensemble un groupe d'experts, travaillant sur ​​de gros sites, afin de créer le guide définitif de la performance front-end ?

Et pas simplement un de ces guides ennuyeux faits pour les robots, mais quelque chose d'amusant ? Si on réunissait Briza Bueno (Americanas.com), Davidson Fellipe (Globo.com), Giovanni Keppelen (ex-Peixe Urbano), Jaydson Gomes (Terra), Marcel Duran (Twitter), Mike Taylor (Opera), Renato Mangini (Google), et Sérgio Lopes (Caelum) afin d'avoir les meilleurs références possibles ?

C'est exactement ce que nous avons fait ! Et nous allons vous guider dans cette bataille pour créer des sites encore plus rapides.

Zeno Rocha, chef de projet.

Est-ce que la performance importe vraiment ?

??

Bien sûr que c'est important et vous le savez. Alors pourquoi continuons-nous à faire des sites lents qui provoquent une mauvaise expérience utilisateur ? Ceci est un guide pratique, écrit et dirigé par la communauté, qui vous montrera comment créer des sites web rapides. Ne perdons pas de temps avec des cas de performance impactant le chiffre d'affaire, allons droit au but !

HTML

Évitez le code inline/embedded

Il y a trois façons basiques d'inclure du CSS ou du JavaScript sur votre page :

1) Inline : le CSS est défini dans un attribut style et le JavaScript dans un attribut onclick par exemple, dans n'importe quelle balise HTML ;

2) Embedded : Le CSS est defini dans une balise <style> et le JavaScript dans une balise <script> ;

3) Externe : le CSS est chargé depuis un <link> et le JavaScript depuis un attribut src d'une balise <script>.

Les deux premières options, bien que réduisant le nombre de requêtes HTTP, augmentent en réalité la taille de votre document HTML. Cela peut-être utile quand vous avez peu de CSS/JavaScript et que le coût d'une nouvelle requête serait plus important. Dans ce cas, faites des tests et évaluez si cela conduit bien à un gain de vitesse. De plus, soyez sûr d'analyser le but de votre page et son public. Si par exemple votre page est une campagne temporaire et que vous ne vous attendez pas à ce que les utilisateurs reviennent, le code inlining/embedded vous aidera à réduire le nombre de requêtes HTTP.

> Evitez d'éditer manuellement le CSS/JS au milieu de votre HTML (automatiser le processus avec un outil est préférable).

La troisième option, outre le fait qu'elle améliore l'organisation de votre code, donne la possiblité à votre navigateur de mettre en cache vos fichiers. Cette option doit être préférée dans la majorité des cas, surtout lorsque vous travaillez sur des fichiers volumineux et beaucoup de pages.

> Outils utiles / Réferences

Les styles en haut, les scripts en bas

Quand nous ajoutons les feuilles de style dans le <head>, nous permettons à la page de s'afficher progressivement, ce qui donne l'impression à nos utilisateurs qu'elle se charge rapidement.

<head>
  <meta charset="UTF-8">
  <title>Browser Diet</title>

  <!-- CSS -->
  <link rel="stylesheet" href="style.css" media="all">
</head>

Mais si nous les mettons à la fin de la page, la page sera rendue sans style jusqu'à ce que le CSS soit chargé et appliqué.

Geek #32

En revanche, lorsqu'il s'agit de JavaScript, il est important de placer les scripts au bas de la page parce-qu'ils bloquent le rendu (affichage) pendant leur chargement et exécution.

<body>
  <p>Lorem ipsum dolor sit amet.</p>

  <!-- JS -->
  <script src="script.js"></script>
</body>

> Références

Essayez async

Pour expliquer pourquoi cet attribut est utile pour de meilleurs performances, il vaut mieux comprendre ce qui se passe lorsqu'on ne l'utilise pas.

Geek #20
<script src="example.js"></script>

Dans ce formulaire, la page doit attendre que le script soit complètement téléchargé, parsé et exécuté avant de parser et afficher n'importe quel HTML à la suite. Cela peut augmenter le temps de chargement de vore page de manière siginificative. Parfois ce comportement peut être désiré, mais ce n'est généralement pas le cas.

<script async src="example.js"></script>

Ce script est téléchargé de manière asynchrone pendant que le reste de la page continue d'être analysé. Le script a l'assurance de s'exécuter dès que son téléchargement sera terminé. Gardez à l'esprit que de multiples téléchargements asynchrones ne seront pas exécutés dans un ordre spécifique.

> References

CSS

Minifier vos feuilles de styles

Pour maintenir un code lisible, une bonne idée est d'écrire des commentaires et d'utiliser l'indentation :

.center {
  width: 960px;
  margin: 0 auto;
}

/* --- Structure --- */

.intro {
  margin: 100px;
  position: relative;
}

Mais tout ceci n'a aucun intérêt pour le navigateur. C'est pour cela qu'il faut toujours vous rappeler de minimifier votre CSS par des outils automatiques.

.center{width:960px;margin:0 auto}.intro{margin:100px;position:relative}

Cela va diminuer la taille des fichiers, ce qui se traduira par un temps de chargement, d'analyse et d'exécution plus rapide.

Pour ceux qui utilisent les préprocesseurs comme Sass, Less, et Stylus, il est possible de configurer la sortie de votre CSS compilé afin qu'il soit minifié.

> Outils / Références

Combiner plusieurs fichiers CSS

Une autre bonne pratique pour l'organisation et la maintenance des styles est de les séparer en composants modulaires.

Geek #9
<link rel="stylesheet" href="structure.css" media="all">
<link rel="stylesheet" href="banner.css" media="all">
<link rel="stylesheet" href="layout.css" media="all">
<link rel="stylesheet" href="component.css" media="all">
<link rel="stylesheet" href="plugin.css" media="all">

Cependant, une requête HTTP est nécessaire pour chacun de ces fichiers (et nous savons que les navigateurs ne peuvent télécharger qu'un nombre limité de ressources en parallèle).

<link rel="stylesheet" href="main.css" media="all">

Combinez donc vos CSS. Avoir moins de fichiers produira moins de requêtes et un temps de chargement de page plus rapide.

Vous voulez avoir le meilleur des deux mondes ? Automatisez ce processus à travers un outil de build.

> Outils / Références

Préférez <link> à @import

Il y a deux façons d'inclure une feuille de style externe dans votre page : soit via la balise <link> :

<link rel="stylesheet" href="style.css">

Soit avec la directive @import (à l'intérieur d'une feuille de style externe ou dans une balise <style>) :

@import url('style.css');

Lorsque vous utilisez la seconde option dans une feuille de style externe, le navigateur est incapable de télécharger le reste du fichier en parallèle, ce qui peut bloquer le téléchargement des autres contenus.

> Références

JavaScript

Charger les librairies tiers de façon asynchrone

Qui n'a jamais chargé du contenu extérieur pour incorporer une vidéo Youtube ou un bouton like/tweet?

Geek #46

Le gros problème est que ce code n'est pas tout le temps chargé de façon optimale, soit à cause de la connexion de l'utilisateur, soit à cause de la connexion au serveur. Le service pourrait aussi être indisponible temporairement, ou même bloqué par le firewall d'une entreprise.

Pour éviter que cela devienne un problème critique quand le site se charge ou pire, que la page se bloque, il est préférable de toujours charger ce script de façon asynchrone (ou d'utiliser des iFrames).

var script = document.createElement('script'),
scripts = document.getElementsByTagName('script')[0];

script.async = true;
script.src = url;
scripts.parentNode.insertBefore(script, scripts);

Si vous souhaitez charger plusieurs widgets, vous pouvez les télécharger de façon asynchrone en utilisant ce script.

> Video / References

Mettre en cache la longueur d'un tableau

La boucle est sans doute l'un des aspects les plus importants en ce qui concerne la perfomance en JavaScript. Essayez d'optimiser la logique dans la boucle pour que chaque itération soit effectuée de façon optimale.

Une façon de faire ceci est de garder en mémoire la taille du tableau. Ainsi, elle n'est pas recalculée à chaque fois qu'on itère dans la boucle.

var arr = new Array(1000),
    len, i;

for (i = 0; i < arr.length; i++) {
  // Mauvais - la taille doit être calculée 1000 fois
}

for (i = 0, len = arr.length; i < len; i++) {
  // Bien - la taille est calculée qu'une seule fois et ensuite gardée en cache
}

> Results on JSPerf

> A Noter : Bien que les navigateurs de recherche actuels optimisent ce processus automatiquement, ceci demeure une bonne méthode pour convenir aux navigateurs plus anciens qui subsistent.

Ceci est particulièrement utile pour les itérations effectuées dans une collection de noeuds (NodeList) en HTML, générée par document.getElementsByTagName('a'). Ces collections sont considérées comme étant "live", c'est-à-dire qu'elles sont automatiquement modifiées quand il y a des changements dans l'élément auquel elles appartiennent.

var links = document.getElementsByTagName('a'),
    len, i;

for (i = 0; i < links.length; i++) {
  // Pas super - à chaque itération la liste de liens sera recalculée pour voir si elle a changée
}

for (i = 0, len = links.length; i < len; i++) {
  // Bien - la taille de la liste est d'abord obtenue et gardée en mémoire, ensuite la boucle est éxecutée
}

// Terrible: boucle infinie
for (i = 0; i < links.length; i++) {
  document.body.appendChild(document.createElement('a'));
  // à chaque itération la liste de liens augmente, la condition de terminaison de la boucle ne sera jamais vérifiée
  // ceci n'aurait pas lieux si la taille de la liste était mise en mémoire cache et utilisée comme la condition d'éxecution de la boucle
}

> References

Evitez document.write

L'utilisation de document.write oblige la page à être chargée intégralement pour qu'il soit éxecuté.

Cette (mauvaise) pratique a été abandonnée depuis des années par les développeurs, mais il y a encore des cas où sont utilisation est toujours obligatoire, comme un plan B pour le chargement asynchrone de fichiers JavaScript.

HTML5 Boilerplate, par exemple, utilise cette technique pour charger jQuery localement si le CDN de Google ne répond pas.

<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script>
<script>window.jQuery || document.write('<script src="js/vendor/jquery-1.9.0.min.js"><\/script>')</script>

> Attention: L'invocation de document.write pendant ou après l'évènement window.onload remplace l'intégralité du contenu de la page.

<span>foo</span>
<script>
  window.onload = function() {
    document.write('<span>bar</span>');
  };
</script>

Le résultat de la page finale sera seulement bar et non foobar comme on peut s'y attendre. La même chose se produit quand ceci est exécuté après l'évènement window.onload.

<span>foo</span>
<script>
  setTimeout(function() {
    document.write('<span>bar</span>');
  }, 1000);
  window.onload = function() {
  // ...
};
</script>

Le résultat sera le même que celui de l'exemple précédent si la fonction planifiée par 'setTimeout' est exécutée après l'évènement window.onload.

> References

Minimiser les "repaints" et "reflows"

Les "repaints" et "reflows" ont lieu lorsque le DOM est ré-actualisé, c'est-à-dire quand une certaine propriété ou élément est modifié.

Les "repaints" ont lieu lorsque le style d'un élèment est modifié sans que la mise en page ne le soit. Nicole Sullivan décrit ceci comme un changement de propriété tel que 'background-color'.

Les "reflows" sont assez coûteux, puisqu'ils sont causés par le changement de la mise en page, telle que la largeur d'un élèment.

Il n'y a pas de doute que l'excès de "reflows" et de "repaints" devrait être évité, plutôt que d'écrire ceci :

Geek #55
var div = document.getElementById("to-measure"),
    lis = document.getElementsByTagName('li'),
    i, len;

for (i = 0, len = lis.length; i < len; i++) {
  lis[i].style.width = div.offsetWidth + 'px';
}

Faites plutôt cela :

var div = document.getElementById("to-measure"),
    lis = document.getElementsByTagName('li'),
    widthToSet = div.offsetWidth,
    i, len;

for (i = 0, len = lis.length; i < len; i++) {
  lis[i].style.width = widthToSet + 'px';
}

Quand on fixe style.width, le navigateur doit recalculer la mise en page. Habituellement, changer le style de plusieurs éléments entraine un seul "reflow" puisque le navigateur n'a pas besoin de penser à cela tant que l'affichage n'est pas modifié. Cependant, dans le premier exemple, on prend offsetWidth qui est la largeur de cet élément, donc le navigateur doit recalculer la mise en page.

Si on doit lire des instances de la mise en page du document, il faut le faire avant d'assigner quoi que ce soit qui puisse changer la mise en page, comme le montre le deuxième exemple.

> Demo / References

Evitez des manipulations inutiles du DOM

A chaque fois que vous touchez au DOM sans en avoir besoin un panda meurt.

Plus sérieusement, naviguer dans le DOM est coûteux. Bien que les moteurs JavaScript deviennent de plus en plus rapides, préférez toujours l'optimisation du nombre de requêtes dans l'arborescence du DOM.

Une des solutions les plus simples à adopter, lorsque qu'un élément doit être atteint plus d'une fois, consiste à le sauvegarder dans une variable. Ainsi vous n'exécutez qu'une requête dans le DOM.

// Très mauvais!
for (var i = 0; i < 100; i++) {
  document.getElementById("myList").innerHTML += "<span>" + i + "</span>";
}
// Bien mieux :)
var myList = "";

for (var i = 0; i < 100; i++) {
  myList += "<span>" + i + "</span>";
}

document.getElementById("myList").innerHTML = myList;
// Bien Bien mieux :)
var myListHTML = document.getElementById("myList").innerHTML;

for (var i = 0; i < 100; i++) {
  myListHTML += "<span>" + i + "</span>";
}

> Resultats sur JSPerf

Minifiez votre JavaScript

Tout comme en CSS, pour obtenir du code lisible, c'est une bonne idée d'écrire des commentaires et d'utiliser de l'indentation :

BrowserDiet.app = function() {
  var foo = true;
    return {
      bar: function() {
        // faire quelque chose
    }
  };
};

Mais tout ceci n'a aucun intérêt pour le navigateur. C'est pour cela qu'il faut toujours vous rappeler de minifier votre JavaScript par des outils automatiques.

BrowserDiet.app=function(){var a=!0;return{bar:function(){}}}

Cela réduira la taille de votre fichier, ce qui accélérera le téléchargement, le traitement et l'exécution de votre page.

> Outils utiles / References

Combinez plusieurs fichiers JS en un seul

Une autre bonne méthode pour l'organisation et la maintenance de scripts est de les séparer en composants modulaires.

Geek #47
<script src="navbar.js"></script>
<script src="component.js"></script>
<script src="page.js"></script>
<script src="framework.js"></script>
<script src="plugin.js"></script>

Cependant, une requête HTTP est nécessaire pour chacun de ces fichiers (et nous savons que les navigateurs ne peuvent télécharger qu'un nombre limité de ressources en parallèle).

<script src="main.js"></script>

Vous devriez donc combiner votre JS. Avoir un nombre plus petit de fichiers entrainera un nombre de requêtes inférieure et une exécution plus rapide.

Vous voulez avoir le meilleur des deux mondes ? Automatisez ce processus avec un outil de build.

> Outils utiles / References

jQuery

Toujours utiliser la dernière version de jQuery

L'équipe en charge du noyau de jQuery cherche constamment à améliorer la performance de la librairie, en utilisant du code plus lisible, de nouvelles fonctionnalités et en optimisant les algorithmes existants.

Geek #36

Pour cette raison, utilisez toujours la dernière version de jQuery, elle est toujours disponible ici si vous voulez la copier localement :

http://code.jquery.com/jquery-latest.js

Mais ne déclarez jamais cette URL dans une balise <script>, cela vous posera des problèmes dans le futur car la dernière version se retrouvera sur votre site dès qu'elle sortira, avant même que vous n'ayez pu la tester. Mettez donc plutôt un lien vers la dernière version spécifique de jQuery dont vous avez besoin.

<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>

Comme le sage Barney Stinson le dit, "Le nouveau, c'est toujours mieux" :P

> References

Selectors

Selectors is one of the most important issues in the use of jQuery. There are many different ways to select an element from the DOM, but that doesn't mean they have the same performance, you can select an element using classes, IDs or methods like find(), children().

Among all of them, select an ID is the fastest one, because its based on a native DOM operation:

$("#foo");

> Results on JSPerf

Utilisez for au lieu de each

L'utilisation de fonction native en JavaScript conduit presque toujours à un résultat plus rapide à l'exécution qu'en utilisant jQuery. Pour cette raison, au lieu d'utililser la méthode jQuery.each, préférez la version JavaScript for pour les boucles.

Mais attention car, si for in est natif, sa performance sera moindre qu'un jQuery.each dans beaucoup de situations.

Et la bonne vielle boucle for nous donne la possibilité d'accélerer les choses en mettant en cache la longueur de la collection sur laquelle on va itérer.

for ( var i = 0, len = a.length; i < len; i++ ) {
    e = a[i];
}

L'utilisation de while inversé et de boucle for inversée est un sujet sensible dans la communauté et ces itérations sont souvent cités comme les formes les plus rapides. Cependant, on les évite souvent car elles sont moins lisibles.

// while inversé
while ( i-- ) {
  // ...
}

// for inversé
for ( var i = array.length; i--; ) {
  // ...
}

Check who has the best performance on the link below: > Resultats sur JSPerf / References

N'utilisez pas toujours jQuery

Parfois du pur JavaScript peut-être plus simple que jQuery.

Geek #6

Considérez le html suivant :

<div id="text">Changeons ce texte</div>

Plutôt que de faire ceci :

$('#text').html('Le texte a changé').css({
  backgroundColor: 'red',
  color: 'yellow'
});

Utilisez du pur JavaScript :

var text = document.getElementById('text');
text.innerHTML = 'Le texte a changé';
text.style.backgroundColor = 'red';
text.style.color = 'yellow';

C'est simple et BEAUCOUP plus rapide. Jetez donc un oeil aux résultats avec JSPerf.

> Resultats avec JSPerf

Images

Utiliser des sprites pour le CSS

Cette technique consiste à grouper plusieurs images dans un seul fichier.

CSS Sprite Example

Et ensuite de les positionner avec CSS.

.icon-foo {
  background-image: url('mySprite.png');
  background-position: -10px -10px;
}

.icon-bar {
  background-image: url('mySprite.png');
  background-position: -5px -5px;
}

Par conséquent, vous réduisez considérablement le nombre de requêtes HTTP et évitez d'éventuels délais de téléchargement d'autres ressources sur votre page.

Lorsque vous utilisez votre sprite, évitez de laisser trop d'espace entre les images. Cela n'affectera pas la taille de votre fichier, mais aura un effet sur la consommation de la mémoire cache.

Bien que cette technique soit assez connue, son utilisation n'est pas très répandue–ce qui est peut-être dû au fait que les développeurs n'utilisent pas d'outils automatisés pour générer des sprites. Nous en avons sélectionné quelques-uns qui peuvent vous aider à faire ceci.

> Outils utiles / References

Data URI

Cette technique est une alternative à l'utilisation des sprites CSS.

Un Data-URI est un moyen d'intégrer le contenu de l'URI que vous donneriez au navigateur en temps normal. Dans cet exemple, on l'utilise pour intégrer le contenu des images d'arrière plan en CSS afin de réduire le nombre de requêtes HTTP requises pour charger la page.

Avant :

.icon-foo {
  background-image: url('foo.png');
}

Après :

.icon-foo {
  background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEUAAACnej3aAAAAAXRSTlMAQObYZgAAAApJREFUCNdjYAAAAAIAAeIhvDMAAAAASUVORK5CYII%3D');
}

Tous les navigateurs depuis IE8 et haut-dessus supportent cette technique d'encodage en base64.

Cette méthode et celle qui utilise les sprites requièrent des outils de développement pour être maintenables. Cette méthode a l'avantage de ne pas avoir besoin de disposer les images manuellement puisqu'elle les conserve dans des fichiers séparés pendant la phase de développement.

Cependant elle a l'inconvénient d'augmenter la taille de votre HTML/CSS considérablement si vous avez des images volumineuses. Il n'est pas recommendé d'utiliser cette méthode si vous ne gzippez pas votre HTML/CSS pendant les requêtes HTTP puisque le surpoids pourrait contre-balancer la rapidité que vous gagnez en minimisant le nombre de requêtes HTTP.

> Outils utiles / References

Ne re-dimensionnez pas vos images dans la mise en page

Toujours définir les attributs 'width' et 'height' d'une image. Ceci évitera les "repaints" et "reflows" inutiles pendant la mise en page du rendu.

<img width="100" height="100" src="logo.jpg" alt="Logo">

Sachant cela, John Q. Developer ayant une image de taille 700x700px, décide d'utiliser cette technique pour afficher une image de 50x50px.

Ce que M. Developper ne réalise pas, c'est que des dizaines de kilobytes inutiles seront alors envoyés aux clients.

Gardez toujours cela en tête : ce n'est pas parce que vous pouvez définir la largeur et hauteur d'une image en HTML que vous devez le faire pour re-dimensionner de larges images.

> References

Optimisez vos images

Les images contiennent beaucoup d'informations inutiles sur internet. Par exemple, une image JPEG peut avoir les metadata Exif de la caméra (date, modèle, lieu, etc.). Une image PNG contient l'information des couleurs, des metadata et contient parfois même une image miniature embarquée. Rien de tout ceci n'est utilisé par le navigateur et augmente inutilement la taille du fichier.

Il existe des outils pour l'optimisation des images, qui suppriment toutes données inutiles et vous retournent un fichier plus léger sans dégrader la qualité de l'image. On dit dans ce cas qu'ils opèrent une compression sans perte.

Un autre moyen d'optimiser les images est de les compresser au risque de perdre en qualité visuelle. On appelle cela des optimisations avec pertes. Quand vous exportez une image JPEG, par exemple, vous pouvez choisir le niveau de qualité (un nombre entre 0 et 100). En pensant à la performance, choisissez toujours le plus petit nombre pour lequel la qualité visuelle est encore acceptable. Une autre technique avec perte commune consiste à réduire la palette des couleurs dans une image PNG ou de convertir les PNG-24 en PNG-8.

Pour améliorer la performance perçue par l'utilisateur, vous devriez aussi convertir tous vos fichiers JPEG en version progressive. Les JPEG progressifs sont très bien supportés dans les navigateurs, sont très simples à créer et n'ont pas de perte significative de qualité. L'avantage est que l'image apparaîtra plus tôt sur la page (Regardez la démo).

> Outils utiles / References

Bonus

Outils de diagnostic: votre meilleur ami

Geek #43

Si vous voulez vous aventurer dans le monde des perfomances web, il est crucial d'installer le extension YSlow dans votre navigateur—ils seront vos meilleurs amis à partir de maintenant.

Ou bien, si vous préférez les outils en ligne, visitez les sites WebPageTest, HTTP Archive, ou PageSpeed.

D'une manière générale, chacun analysera les performances de votre site et créera un rapport qui vous donnera une note couplée avec de précieux conseils pour vous aider à résoudre les problèmes potentiels.

C'est tout pour aujourd'hui !

Geek #31

Nous espérons qu'après avoir lu ce guide, vous pourrez garder votre site en forme. :)

Et souvenez-vous, comme toutes choses dans la vie, il n'y a pas de solution miracle. L'optimisation des performances de votre application est un jeu qui en vaut la chandelle, mais cela ne doit pas être le seul critère de décision—parfois il vous faudra peser le pour et le contre.

Vous voulez en apprendre plus ? Jetez un oeil aux Références que nous avons utilisé pour ce guide.

Des suggestions ? Envoyez un tweet à @BrowserDiet ou une pull request sur Github.

N'oubliez pas de partager avec vos amis, créons un web plus rapide pour tout le monde. \o/