L'équipe Yahoo a identifié un certain nombre de bonnes pratiques pour la création de pages web rapide. La liste comprend 35 des meilleures pratiques divisés en 7 catégories.
Le projet de traductions des règles YSlow est rendu possible par les contributions suivantes:
80% du temps de réponse de l’utilisateur final est passé sur le front-end. La majeure partie de ce temps est consacrée au téléchargement de tous les composants de la page : images, feuilles de style, scripts, flash, etc. Réduire le nombre de composants réduit le nombre de requêtes HTTP nécessaires pour rendre la page. C’est la clé de pages plus rapides.
Une façon de réduire le nombre de composants dans la page est de simplifier la conception de la page. Mais existe-t-il un moyen de créer des pages avec un contenu plus riche tout en conservant des temps de réponse rapides ? Voici quelques techniques pour réduire le nombre de requêtes HTTP, tout en conservant des pages riches d’un point de vue design.
Les fichiers concaténés sont un moyen de réduire le nombre de requêtes HTTP en combinant tous les scripts et toutes les CSS dans une feuille de style unique et en un seul script. la concaténation de fichiers est plus difficile lorsque les scripts et les feuilles de style varient d’une page à l’autre. Inclure ceci dans votre processus de construction des pages améliore les temps de réponse.
L’utilisation des sprites CSS est la meilleure méthode pour réduire le nombre de demandes d’images. Combinez vos images de fond en une seule image et utilisez les propriétés CSS background-image
et background-position
pour afficher le segment de l’image souhaitée.
Les cartes-image combinent plusieurs images en une seule image. La taille globale est à peu près la même, mais la réduction du nombre de requêtes HTTP accélère le chargement de la page. Les cartes-images ne fonctionnent que si les images sont contiguë dans la page, comme un menu de navigation. Définir les coordonnées de chaque segment dans une carte d’image peut être fastidieux et source d’erreurs. L’utilisation de cartes-images pour la navigation casse l’accessibilité de la page, donc non recommandé.
Les images en ligne utilisent le data:
URL scheme pour intégrer les données d’image dans la page en cours. Cela peut augmenter la taille de votre document HTML. Combiner des images en ligne dans vos feuilles de style (en cache) est un moyen de réduire les requêtes HTTP et éviter d’augmenter la taille de vos pages. Les images en ligne ne sont pas encore prise en charge dans tous les navigateurs principaux.
Réduire le nombre de requêtes HTTP de votre page est un point de départ idéal. C’est la recommandation qui permet d’améliorer les performances pour les nouveaux visiteurs de manière la plus significative. Comme décrit dans le billet de blog de Tenni Theurer Browser Cache Usage - Exposed!, 40 à 60% des visiteurs quotidiens de votre site le consultent avec un cache navigateur vide. Améliorer le temps de chargement pour ces nouveaux visiteurs est la clé d’une meilleure expérience utilisateur.
La proximité de l’utilisateur par rapport à votre serveur web a un impact sur les temps de réponse. Le déploiement de votre contenu sur plusieurs serveurs géographiquement dispersés rendra vos pages plus rapides du point de vue de l’utilisateur. Mais par où commencer ?
Dans un premier temps, ne tentez pas la refonte de votre application web pour travailler en mode distribué afin de mettre en œuvre du contenu dispersé géographiquement. Selon l’application, l’évolution de l’architecture pourrait inclure des tâches redoutables telles que la synchronisation d’états de session et la réplication de transactions de base de données de serveurs à serveurs répartis géographiquement. Vous pourriez être retardés, voire ne jamais atteindre l’objectif visant à réduire la distance entre les utilisateurs et le contenu si vous souhaitez passer par cette étape d’amélioration d’architecture applicative.
Rappelez-vous que 80 à 90% du temps de réponse de l’utilisateur final est passé à télécharger tous les composants de la page : images, feuilles de style, scripts, flash, etc. C’est la règle d’or de la performance. Plutôt que de partir sur la refonte de votre architecture applicative, il vaut mieux d’abord répartir votre contenu statique. Non seulement il permet une plus grande réduction des temps de réponse, mais il est de plus facile à mettre en œuvre grâce à des réseaux de diffusion de contenu.
Un réseau de diffusion de contenu (CDN) est un ensemble de serveurs Web répartis sur plusieurs endroits géographiques pour fournir un contenu de façon plus efficace aux utilisateurs. Le serveur sélectionné pour fournir du contenu à un utilisateur spécifique est généralement basé sur une mesure de proximité de réseau. Par exemple, le serveur avec le moins de sauts de réseau ou le serveur avec le meilleur temps de réponse est choisi.
Certaines grandes entreprises de l’Internet possèdent leur propre CDN, mais il peut être moins cher d’utiliser un fournisseur de services CDN, comme Akamai Technologies, EdgeCast ou level3. Pour les entreprises qui démarrent et les sites web privés, le coût d’un service CDN peut être prohibitif. Mais que votre public cible grandisse et devienne plus global, et alors un CDN est nécessaire pour obtenir des temps de réponse rapides. Chez Yahoo!, ceux qui ont déplacé le contenu statique de leurs serveurs d’applications Web à un CDN (à la fois chez des prestataires externes comme ceux mentionnés ci-dessus et leur propre CDN chez Yahoo) améliorent le temps de réponse de l’utilisateur final de 20% ou plus. Le passage à un CDN est un changement de code relativement facile qui va considérablement améliorer la vitesse de votre site web.
Il ya deux aspects à cette règle :
Le design des pages web est de plus en plus riche, ce qui signifie plus de scripts, plus de feuilles de style, plus d’images et de Flash dans la page. Un nouveau visiteur de votre page peut avoir à faire plusieurs requêtes HTTP, mais en utilisant l’en-tête Expire, vous rendez ces composants cachables. Cela évite les requêtes HTTP inutiles sur les pages vues ultérieurement. Les en-têtes Expire sont le plus souvent utilisés avec des images, mais ils doivent être utilisés sur tous les composants, y compris les scripts, les feuilles de style et les composants Flash.
Les navigateurs (et les proxies) utilisent un cache afin de réduire le nombre et la taille des requêtes HTTP, ce qui accélère le chargement des pages. Un serveur web utilise l’en-tête Expire dans la réponse HTTP pour indiquer au client combien de temps un composant peut être mis en cache. Une date lointaine dans l’en-tête Expire indique au navigateur que cette réponse reste valide jusqu’au 15 Avril 2015.
Expires: Thu, 15 Apr 2015 20:00:00 GMT
Si votre serveur est Apache, utilisez la directive ExpiresDefault qui permet de fixer une date d’expiration par rapport à la date actuelle. Cet exemple de directive ExpiresDefault fixe la date d’expiration dans 10 ans à partir du moment de la demande.
ExpiresDefault "access plus 10 years"
Gardez à l’esprit, si vous utilisez un en-tête Expire loin dans le futur que vous devrez changer le nom de fichier du composant à chaque fois que celui-ci est modifié. Chez Yahoo!, un numéro de version est intégré dans le nom de fichier du composant, par exemple yahoo_2.0.6.js.
Utiliser un en-tête Expire loin dans le futur affecte uniquement les pages vues d’un utilisateur revenant sur votre site. Cela n’a aucun effet sur le nombre de requêtes HTTP nécessaires quand un utilisateur visite votre site pour la première fois et que la mémoire cache du navigateur est vide. Par conséquent, l’ impact de cette amélioration de la performance dépend de la fréquence d’utilisateurs réguliers. Ceux-ci possèdent en effet un cache amorcé. (Un «cache amorcé» contient déjà tous les éléments de la page.) Nous avons mesuré ceci chez Yahoo! et trouvé que le nombre de pages vues avec un cache amorcé est de 75 à 85 %. En utilisant un en-tête Expire loin dans le futur, vous augmentez le nombre de composants qui sont mis en cache par le navigateur et re-utilisés sur les vues ultérieures de pages sans envoyer un seul octet sur la connexion Internet de l’utilisateur.
Le temps nécessaire pour transférer une requête HTTP et la réponse sur le réseau peut être sensiblement réduit par les décisions prises par les ingénieurs front-end. Il est vrai que la vitesse de la bande passante de l’utilisateur final, le fournisseur de services Internet, la proximité des points de peering de change, etc. sont hors du contrôle de l’équipe de développement. Mais il y a d’autres variables qui influent sur le temps de réponse. La compression réduit les temps de réponse en diminuant la taille de la réponse HTTP.
À partir de HTTP/1.1, les clients Web indiquent leur support de la compression avec l’en-tête Accept-Encoding dans la requête HTTP.
Accept-Encoding: gzip, deflate
Si le serveur Web voit cet en-tête dans la demande, il peut compresser la réponse en utilisant l’une des méthodes énumérées par le client. Le serveur web informe le client Web de cette compression via l’en-tête Content-Encoding dans la réponse.
Content-Encoding: gzip
Gzip est la méthode de compression la plus populaire et efficace pour le moment. Elle a été développée par le projet GNU et normalisée par la RFC 1952. Le seul autre format de compression que vous pourriez rencontrer est deflate, mais il est moins efficace et moins populaire.
Utiliser Gzip diminue généralement la taille de la réponse d’environ 70%. Environ 90% du trafic Internet d’aujourd’hui se déplace à travers des navigateurs qui prétendent supporter gzip. Si vous utilisez Apache, le module dédié à gzip dépend de votre version : Apache 1.3 utilise mod_gzip alors qu’Apache 2.x utilise mod_deflate.
Il y a des problèmes connus avec les navigateurs et les proxies qui peuvent causer une incohérence entre ce que le navigateur attend et ce qu’il reçoit en contenu compressé. Heureusement, ces cas diminuent en même temps que les navigateurs les plus anciens. Les modules Apache peuvent aider en ajoutant l’en-tête de réponse Vary automatiquement.
Les serveurs décident quoi gzipper en se basant sur le type de fichier, mais ils sont généralement trop restreints dans ce qu’ils décident de compresser. La plupart des sites web gzip leurs documents HTML. Il est également intéressant de gzipper vos scripts et les feuilles de style, ce que ne font pas de nombreux sites Web. En fait, il est intéressant de compresser toute réponse de texte, y compris XML et JSON. Les images et les fichiers PDF ne doivent pas être compressés au format gzip, car ils le sont déjà par d’autres moyens. Essayer de les gzipper entraîne non seulement un gaspillage de CPU mais peut potentiellement augmenter la taille des fichiers résultants.
Gzipper autant que possible les fichiers est un moyen facile de réduire le poids de la page et d’accélérer l’expérience utilisateur.
Dans nos recherches sur la performance chez Yahoo!, nous avons découvert que le déplacement des feuilles de style dans la partie HEAD du document rend les pages apparemment plus rapides à charger. C’est parce que mettre des feuilles de style dans la partie HEAD d’un document permet à la page d’être rendue progressivement.
Les ingénieurs se souciant de performance veulent une page à chargement progressif, c’est ce que nous voulons pour que le navigateur affiche n’importe quel contenu le plus rapidement possible. Ceci est particulièrement important pour les pages avec beaucoup de contenu et pour les utilisateurs de connexions Internet lentes. L’importance de donner aux utilisateurs un retour visuel, tels que des indicateurs de progrès a été bien documenté. Dans notre cas, la page HTML est l’indicateur de progression! Lorsque le navigateur charge la page progressivement, l’en-tête, la barre de navigation, le logo en haut, etc. tous ces éléments servent de feedback visuel pour l’utilisateur qui est en attente de la page. Cela améliore l’expérience globale de l’utilisateur.
Le problème en mettant les feuilles en bas de document est l’interdiction du rendu progressif dans de nombreux navigateurs, y compris Internet Explorer. Ces navigateurs bloquent tout rendu pour éviter d’avoir à redessiner des éléments de la page si leurs styles changent. L’utilisateur est scotché une page blanche.
La spécification HTML stipule clairement que les feuilles de style doivent être incluses dans la partie HEAD de la page. “Contrairement à la balise A, la balise [LINK] ne peut apparaître que dans la section HEAD d’un document mais peut apparaître plusieurs fois.” Aucune des alternatives comme les flashs de contenus non stylés ou le grand écran blanc ne valent de prendre le risque. La solution optimale est de suivre la spécification HTML et de charger vos feuilles de style dans la partie HEAD du document.
Le problème des scripts est qu’ils bloquent les téléchargements en parallèle. La spécification HTTP/1.1 suggère que les navigateurs ne téléchargent pas plus de deux composants en parallèle par nom d’hôte. Si vous servez vos images à partir de plusieurs noms d’hôtes, vous pouvez obtenir plus de deux téléchargements en parallèle. Cependant, pendant le téléchargement d’un script, le navigateur ne démarre pas d’autres téléchargements y compris sur différents noms de domaine.
Dans certaines situations, il n’est pas facile de déplacer ses scripts en bas de page. Si, par exemple, le script utilise document.write
pour insérer une partie du contenu de la page, il ne peut pas être déplacé plus bas dans la page. Il pourrait s’ensuivre des problèmes de visibilité. Dans de nombreux cas, il existe des moyens de contourner ces situations.
Une autre suggestion qui revient souvent est l’utilisation de scripts différés. L’attribut DEFER
indique que le script ne contient pas document.write. C’est une indication pour les navigateurs qu’ils peuvent continuer le rendu de la page. Malheureusement, Firefox ne prend pas en charge l’attribut DEFER
. Dans Internet Explorer, le script peut être différé mais pas autant que vous le souhaitez. Si un script peut être différé, il peut également être déplacé vers le bas de la page. Cela rendra vos pages Web plus rapides à charger.
Beaucoup des règles de performance traitent de la façon dont les composants externes sont gérés. Toutefois, avant que ces éléments apparaissent, vous devez vous poser une question plus fondamentale : les scripts JavaScript et les feuilles de style CSS doivent-ils être contenus dans des fichiers externes ou en ligne dans la page elle-même ?
L’utilisation des fichiers externes produit généralement des pages plus rapides parce que les fichiers JavaScript et CSS sont mis en cache par le navigateur. JavaScript et CSS en ligne dans les documents HTML sont téléchargés chaque fois que le document HTML est demandé. Cela réduit le nombre de requêtes HTTP nécessaires mais augmente la taille du document HTML. D’autre part, si le code JavaScript et CSS dans des fichiers externes est mis en cache par le navigateur, la taille du document HTML est réduite sans augmenter le nombre de requêtes HTTP.
Le facteur-clé est donc la fréquence avec laquelle les composants CSS et JavaScript externes sont mis en cache par rapport au nombre de documents HTML demandés. Ce facteur, bien que difficile à quantifier, peut être mesuré à l’aide de divers paramètres. Si les utilisateurs de votre site voient plusieurs pages par session et que nombre de celles-ci ré-utilisent les mêmes scripts et feuilles de style, alors il y a un plus grand bénéfice potentiel à utiliser des fichiers externes en cache.
De nombreux sites Web échouent sur ces mesures. Pour ces sites, la meilleure solution consiste généralement à déployer le code JavaScript et les feuilles de style CSS dans des fichiers externes. La seule exception où les mettre en ligne est préférable est les pages d’accueil, tels que Yahoo! et Mon Yahoo! . Les pages d’accueil qui ont peu de vues (voir une seule) par session peuvent trouver que JavaScript et CSS en ligne résultent dans des temps de réponse plus rapides pour l’utilisateur final.
Pour les pages d’accueil qui sont généralement les premières d’une suite de pages vues, il existe des techniques qui s’appuient sur la réduction de requêtes HTTP que fournit le mode en ligne ainsi que les avantages de la mise en cache obtenus grâce à l’utilisation de fichiers externes. Une de ces techniques consiste à charger en ligne le JavaScript et les CSS dans la page d’accueil mais en téléchargeant dynamiquement les fichiers externes après le chargment de la page. Les pages suivantes pourraient alors référencer les fichiers externes qui devraient déjà être dans le cache du navigateur.
Les expressions CSS sont un moyen puissant (et dangereux) pour définir des propriétés CSS dynamiquement. Elles ont été prises en charge dans Internet Explorer à partir de la version 5, mais ont été rendues obsolètes à partir de IE8. Par exemple, la couleur de fond peut être configurée pour alterner toutes les heures en utilisant une expression CSS :
background-color: expression( (new Date()).getHours()%2 ? "#B8D4FF" : "#F08A00" );
Comme démontré ci-dessus, la méthode expression
accepte une expression JavaScript. La propriété CSS est réglée sur le résultat de l’évaluation de l’expression JavaScript. La méthode expression
est ignorée par les autres navigateurs, elle est donc utile pour définir les propriétés d’Internet Explorer nécessaires pour créer une expérience cohérente à travers les autres navigateurs.
Le problème avec les expressions, c’est qu’elles sont évaluées plus souvent que ce à quoi la plupart des gens pourraient s’attendre. Non seulement elles sont évaluées lorsque la page est rendue et redimensionnée mais aussi lorsque la page défile et même lorsque l’utilisateur déplace la souris sur la page. Ajouter un compteur à l’expression CSS nous permet de garder une trace de quand et combien de fois une expression CSS est évaluée. Déplacer la souris dans la page peut facilement générer plus de 10.000 évaluations.
Une façon de réduire le nombre de fois que votre expression CSS est évaluée est d’utiliser des expressions uniques dans le temps. La première fois l’expression est évaluée, elle définit la propriété de style pour une valeur explicite qui remplace l’expression CSS. Si la propriété de style doit être définie de façon dynamique tout au long de la vie de la page, utilisez comme approche alternative des gestionnaires d’événements au lieu d’expressions CSS. Si vous devez utiliser des expressions CSS, rappelez-vous qu’elles peuvent être évaluées des milliers de fois et pourraient affecter la performance de votre page.
Le système de noms de domaines (DNS) fait correspondre les noms d’hôtes en adresses IP, tout comme les carnets d’adresse font correspondre les noms des personnes à leurs numéros de téléphone. Lorsque vous tapez www.yahoo.com dans votre navigateur, un résolveur DNS contacté par le navigateur renvoie l’adresse IP de ce serveur. Les requêtes DNS ont un coût. Il faut généralement 20 à 120 millisecondes à un serveur DNS pour rechercher l’adresse IP d’un nom d’hôte donné. Le navigateur ne peut rien télécharger depuis cet hôte tant que la recherche DNS n’est pas terminée.
Les recherches DNS sont mises en cache pour améliorer les performances. Cette mise en cache peut se produire sur un serveur de mise en cache dédié maintenu par le réseau du FAI ou local de l’utilisateur. Il y a aussi la mise en cache qui se produit sur l’ordinateur de l’utilisateur. Les informations DNS restent dans le cache DNS du système d’exploitation (le «service Client DNS» sur Microsoft Windows). La plupart des navigateurs ont leurs propres caches distincts de la mémoire cache du système d’exploitation. Tant que le navigateur conserve un enregistrement DNS dans son propre cache, ça ne dérange pas le système d’exploitation avec une demande d’enregistrement.
Par défaut, Internet Explorer met en cache les recherches DNS pendant 30 minutes, comme spécifié par le paramètre DnsCacheTimeout
de la base des registres. Firefox met en cache les recherches DNS pendant 1 minute, contrôlé par le paramètre de configuration network.dnsCacheExpiration
. (Fasterfox change ce paramètre à 1 heure.)
Lorsque le cache DNS du client est vide (à la fois le navigateur et le système d’exploitation), le nombre de requêtes DNS est égal au nombre de noms d’hôtes uniques dans la page web. Cela inclut les noms d’hôtes utilisés dans l’URL de la page, des images, des fichiers de scripts, feuilles de style, objets Flash, etc. Réduire le nombre de noms d’hôtes uniques réduit le nombre de requêtes DNS.
Réduire le nombre de noms d’hôtes uniques peut réduire le nombre de téléchargements parallèles effectués par la page. Limiter les recherches DNS réduit les temps de réponse mais la réduction du nombre de téléchargements parallèles peut augmenter les temps de réponse. Ma ligne directrice est de diviser ces composants sur au moins deux mais pas sur plus de quatre noms d’hôtes. Il en résulte un bon compromis entre réduction des requêtes DNS et pourcentage élevé de téléchargements parallèles.
La minification consiste à enlever des caractères inutiles à partir du code afin de réduire sa taille; ce qui améliore les temps de chargement. Lorsque le code est minifié, tous les commentaires sont supprimés ainsi que les caractères inutiles ou blancs (espaces, sauts de ligne, tabulations). Dans le cas du langage JavaScript, ceci améliore les performances de temps de réponse en raison de la taille du fichier téléchargé plus réduite. Deux outils populaires pour minifier le code JavaScript sont JSMin et YUI Compressor. Le compresseur YUI PEUT aussi minifier les CSS.
L’obfuscation est une alternative qui peut être appliquée au code source. Plus complexe que la minification et donc plus susceptible de générer des bugs à la suite de l’étape de brouillage proprement dite. Dans une enquête sur dix sites web américains, la minification permet une réduction de taille de 21% contre 25% pour l’obfuscation. Bien que l’obfuscation ait une réduction de taille supérieure, il reste moins risqué de minifier le JavaScript.
En plus de minifier les scripts externes et les feuilles de style, les blocs en ligne <script>
et <style>
peuvent et doivent également être minifiés. Même si vous gzippez les scripts et les styles, les minifier en plus permet encore de réduire la taille de 5% ou plus. Vu que l’utilisation et la taille de code JavaScript et de feuilles de style CSS augmente, les économies réalisées par minification du code sont plus grandes.
Les redirections sont effectuées en utilisant les codes d’état 301 et 302 . Voici un exemple d’en-têtes HTTP contenus dans une réponse 301 :
HTTP/1.1 301 Moved Permanently
Location: http://example.com/newuri
Content-Type: text/html
Le navigateur conduit automatiquement l’utilisateur vers l’URL spécifiée dans le champ Location
. Toutes les informations nécessaires pour une redirection sont contenues dans les en-têtes. Le corps de la réponse est généralement vide. Malgré leur nom, aucune réponse 301 ou 302 n’est mise en cache dans la pratique, sauf si des en-têtes supplémentaires, comme Expire
ou Cache-Control
indiquent qu’elle devrait l’être. La balise meta refresh et JavaScript sont d’autres façons de diriger les utilisateurs vers une URL différente, mais si vous devez faire une redirection, la technique préférée est d’utiliser les codes d’état HTTP 3xx standards, principalement pour s’assurer que le bouton de retour du navigateur fonctionne correctement.
La principale chose à retenir est que les redirections ralentissent l’expérience utilisateur. L’insertion d’une redirection entre l’utilisateur et le document HTML retarde tout le rendu de la page puisqu’aucun composant ne peut commencer à être téléchargé jusqu’à ce que le document HTML ne soit arrivé.
L’une des redirections les plus inutiles est aussi l’une des plus fréquentes (et les développeurs web ne sont généralement pas au courant). Elle se produit quand une barre oblique (/) est absente depuis une URL qui devrait en avoir une. Par exemple, aller à http://astrology.yahoo.com/astrology (notez le slash ajouté). Ce problème est résolu dans Apache en utilisant les directives Alias
, mod_rewrite
ou DirectorySlash directive
.
La connexion d’un ancien site Web vers un nouveau est un autre usage répandu pour les redirections. D’autres usages consistent à relier les différentes parties d’un site Web et de diriger l’utilisateur en fonction de certaines conditions (type de navigateur, type de compte utilisateur, etc.). Utiliser une redirection pour connecter deux sites Web est simple et nécessite peu de code supplémentaire. Bien que l’utilisation des redirections dans ces situations réduit la complexité pour les développeurs, elle dégrade l’expérience utilisateur. Si la redirection a lieu sur le même serveur, il est possible d’utiliser Alias
et mod_rewrite
comme alternatives pour cette utilisation de redirections. Si un changement de nom de domaine est la cause de l’utilisation de redirections, une alternative est de créer un CNAME (un enregistrement DNS qui crée un pointage d’alias de nom d’un domaine à l’autre) en combinaison avec Alias
ou mod_rewrite
.
Inclure le même fichier JavaScript à deux reprises dans une seule page nuit à la performance. Ce n’est pas aussi rare qu’on pourrait le penser. Un examen des dix meilleurs sites web américains montre que deux d’entre eux contiennent un script dupliqué. Deux principaux facteurs augmentent les chances d’un script d’être dupliqué dans une page Web : la taille de l’équipe et le nombre de scripts. Lorsque cela arrive, le doublon met à mal les performances en créant des requêtes HTTP inutiles et l’exécution de code JavaScript perdu.
Des requêtes HTTP inutiles se produisent dans Internet Explorer mais pas dans Firefox. Dans Internet Explorer, si un script externe est inclus deux fois et n’est pas mis en cache, il génère deux requêtes HTTP lors du chargement de la page. Même si le script est mis en cache, des requêtes HTTP supplémentaires se produisent lorsque l’utilisateur recharge la page.
En plus de générer des requêtes HTTP inutiles, du temps est perdu à interpréter le script plusieurs fois. Cette exécution JavaScript redondante se passe dans Firefox et Internet Explorer, indépendamment du fait que le script est mis en cache.
Une façon d’éviter d’inclure accidentellement le même script deux fois est de mettre en place un module de gestion de scripts dans votre système de templates. La manière typique d’inclure un script est d’utiliser la balise SCRIPT dans votre page HTML.
<script type="text/javascript" src="menu_1.0.17.js"></script>
Une alternative en PHP serait de créer une fonction appelée insertScript
.
<?php insertScript("menu.js") ?>
En plus de prévenir le même script d’être inséré à plusieurs reprises, cette fonction pourrait traiter d’autres choses avec des scripts, tels que le contrôle des dépendances et l’ajout de numéros de version pour les noms de fichiers de scripts. Ceci afin de bénéficer des en-têtes Expire.
Les balises d’entités (ETAG) sont un mécanisme que les serveurs web et les navigateurs utilisent pour déterminer si le composant dans le cache du navigateur correspond à celui sur le serveur d’origine (Une «entité» est un autre mot pour désigner un «composant» : image, script, feuille de style, etc). Les ETags ont été ajoutés pour fournir un mécanisme de validation des entités qui est plus souple que la date de dernière modification. Un ETag est une chaîne qui identifie de manière unique une version spécifique d’un composant. Les seules contraintes de format sont que la chaîne soit indiquée entre guillements. Le serveur d’origine spécifie le ETag du composant en utilisant ETag
dans l’en-tête de réponse.
HTTP/1.1 200 OK
Last-Modified: Tue, 12 Dec 2006 03:03:59 GMT
ETag: "10c24bc-4ab-457e1c1f"
Content-Length: 12195
Plus tard, si le navigateur doit valider un composant, il utilise l’en-tête If-None-Match
pour passer le ETag vers le serveur d’origine. Si les ETags correspondent, un code de statut 304 est renvoyé, économisant 12 195 octets dans la réponse pour cet exemple.
GET /i/yahoo.gif HTTP/1.1
Host: us.yimg.com
If-Modified-Since: Tue, 12 Dec 2006 03:03:59 GMT
If-None-Match: "10c24bc-4ab-457e1c1f"
HTTP/1.1 304 Not Modified
Le problème avec les ETags est qu’ils sont généralement construits en utilisant des attributs qui les rendent uniques à un serveur, unique à l’hébergement d’un site. Les ETags ne correspondent pas lorsqu’un navigateur obtient le composant d’origine d’un serveur et essaie de valider ce composant sur un serveur différent, une situation très fréquente sur les sites Web qui utilisent un cluster de serveurs pour traiter les demandes. Par défaut, Apache et IIS intégrent les données dans le ETag, ce qui réduit considérablement les chances de réussite des tests de validité sur les sites Web avec plusieurs serveurs.
Le format ETag pour Apache 1.3 et 2.x est inode-size-timestamp
. Même si un fichier peut résider dans le même répertoire sur plusieurs serveurs, et avoir la même taille, les mêmes permissions et le même horodatage, son inode est différent d’un serveur à un autre.
IIS 5.0 et 6.0 ont un problème semblable avec ETags. Le format de ETags sur IIS est Filetimestamp:ChangeNumber
. ChangeNumber
est un compteur utilisé pour suivre les changements de configuration de IIS. Il est peu probable que le ChangeNumber
soit le même sur tous les serveurs IIS derrière un site web.
Le résultat final pour les ETags générés par Apache et IIS pour le même composant ne correspondent pas d’un serveur à un autre. Si les ETags ne correspondent pas, l’utilisateur ne reçoit pas la petite et rapide réponse 304 pour lesquels ils ont été prévus. Au contraire, ils obtiendront une réponse normale 200 avec toutes les données pour le composant. Si vous hébergez votre site web sur un seul serveur, ce n’est pas un problème. Mais si vous avez plusieurs serveurs d’hébergement pour votre site web, et que vous utilisez Apache ou IIS avec la configuration ETag par défaut, les utilisateurs ont des pages plus lentes, vos serveurs ont une charge plus élevée, vous consommez plus de bande passante, et les proxies ne peuvent mettre en cache votre contenu de manière efficace. Même si vos composants ont un en-tête Expire
lointain, une requête GET conditionnelle est toujours faite à chaque fois que l’utilisateur appuie sur Recharger ou Actualiser.
Si vous ne tirez pas parti du modèle de validation flexible que les ETags fournissent, il est préférable de simplement retirer les ETag partout. L’en-tête Last-Modified
valide sur la base de l’horodatage du composant. Retirer les ETag réduit à la fois la taille des en-têtes HTTP de la réponse et des demandes ultérieures. Cet article du support technique Microsoft décrit comment supprimer les ETags. Dans Apache, cela se fait simplement en ajoutant la ligne suivante à votre fichier de configuration d’Apache :
FileETag none
Un des avantages cités d’Ajax est qu’il fournit une rétroaction instantanée à l’utilisateur car il demande des informations de manière asynchrone à partir du serveur Web. Cependant, utiliser Ajax n’est pas une garantie que l’utilisateur ne sera pas en train de se tourner les pouces en attendant le retour des réponses Asynchronous JavaScript and XML. Dans de nombreuses applications, maintenir l’attente de l’utilisateur dépend de la façon dont Ajax est utilisé. Par exemple, dans un client de messagerie basé sur le Web, l’utilisateur sera maintenu en attendant les résultats d’une requête Ajax pour trouver tous les messages électroniques correspondant aux critères de recherche. Il est important de se rappeler que «asynchrone» ne signifie pas «instantané».
Pour améliorer les performances, il est important d’optimiser ces réponses Ajax. Le moyen le plus important pour améliorer les performances Ajax est de servir les réponses mises en cache, comme indiqué dans Ajouter une expiration ou un en-tête Cache-Control. D’autres règles s’appliquent également à pour Ajax:
Voyons un exemple. Un client de messagerie Web 2.0 peut utiliser Ajax pour télécharger le carnet d’adresses de l’utilisateur à des fins d’auto-complétion. Si l’utilisateur n’a pas modifié son carnet d’adresse depuis la dernière fois, la réponse du carnet d’adresses précédente pourrait être lue à partir du cache si cette réponse Ajax a été mise en cache avec un en-tête Expire ou Cache-Control. Le navigateur doit être informé quand utiliser une réponse de carnet d’adresses précédemment mise en cache par rapport à en demander une nouvelle. Cela pourrait se faire par l’ajout d’un horodatage dans le carnet d’adresse URL Ajax indiquant la dernière fois que l’utilisateur a modifié son carnet d’adresses. Par exemple, &t=1190241612
. Si le carnet d’adresses n’a pas été modifié depuis le dernier téléchargement, l’horodatage sera le même et le carnet d’adresses sera lu à partir de la mémoire cache du navigateur, éliminant un aller-retour HTTP supplémentaire. Si l’utilisateur a modifié son carnet d’adresses, l’horodatage assure que la nouvelle URL ne correspond pas à la réponse mise en cache, et le navigateur demandera une nouvelle réponse du carnet d’adresses à jour .
Même si vos réponses Ajax sont créées dynamiquement, et qu’elles ne sontt applicables qu’à un seul utilisateur, elles peuvent encore être mises en cache. Cela rendra vos applications Web 2.0 plus rapides.
Lorsque les utilisateurs demandent une page, il peut se passer 200 à 500 ms sur le serveur pour assembler la page HTML. Pendant ce temps, le navigateur est inactif, en attente des données qui vont arriver. En PHP vous avez la fonction flush(). Elle vous permet d’envoyer une partie de la réponse HTML afin que le navigateur puisse commencer la récupération des composants pendant que votre serveur web est occupé avec le reste de la page HTML. L’avantage est surtout visible sur les serveurs assez chargés et sur les clients légers.
Un bon endroit pour nettoyer le tampon est juste après la partie HEAD du document. Le code HTML contenu dans cette partie est généralement plus facile à produire et il vous permet d’inclure tous les fichiers CSS et JavaScript. Le navigateur peut commencer à charger en parallèle alors que le serveur web est en cours de traitement.
Exemple:
... <!-- css, js -->
</head>
<?php flush(); ?>
<body>
... <!-- content -->
Yahoo! search a été un des pionniers au niveau recherche sur les tests utilisateurs afin de prouver les avantages de l’utilisation de cette technique.
L’équipe de Yahoo! Mail a constaté que lors de l’utilisation de XMLHttpRequest
, POST est mis en œuvre dans les navigateurs comme un processus en deux étapes : l’envoi des en-têtes puis l’envoi de données. Il est donc préférable d’utiliser GET, qui ne prend qu’un paquet TCP pour envoyer (sauf si vous avez un grand nombre de cookies). La longueur maximale pour une URL dans IE est 2K, donc si vous envoyez des données de plus de 2K vous pourriez ne pas être en mesure d’utiliser GET.
Un côté intéressant est que POST sans affichage réel de données se comporte comme GET. Sur la base des spécifications HTTP, GET est destiné à récupérer des informations. Il serait donc très judicieux (sémantiquement) d’utiliser GET lorsque vous récupérez des données, par opposition à l’envoi de données à stocker côté serveur.
Vous pouvez jeter un oeil de plus près à votre page et vous vous demandez : «Qu’est-ce qui est absolument nécessaire afin d’initier le rendu de la page?». Le reste du contenu et des composants peut attendre.
JavaScript est un candidat idéal pour la séparation avant et après l’événement onload. Par exemple, si vous avez du code JavaScript et des bibliothèques qui font du glisser-déposer et autres animations, celles-ci peuvent attendre. Glisser des éléments sur la page vient après le rendu initial. Autres endroits pour chercher des candidats pour le post chargement : le contenu caché (il apparaît après une action de l’utilisateur) et les images en-dessous de la ligne de flottaison.
Les outils pour vous aider dans votre effort : YUI Image Loader vous permet de retarder les images sous la ligne de flottaison et le YUI Get Utility est un moyen facile d’inclure du JS et des CSS à la volée. Pour voir un exemple, jetez un oeil à La page d’accueil de Yahoo! avec le panneau réseau de Firebug activé.
C’est une bonne chose que les objectifs de performance soient en adéquation avec d’autres bonnes pratiques de développement web. Dans ce cas, l’idée d’améliorations progressives nous dit que le JavaScript, quand il est supporté, peut améliorer l’expérience utilisateur. Mais vous devez vous assurer que la page fonctionne même sans JavaScript. Ainsi, après que vous ayez fait en sorte que la page fonctionne, vous pouvez l’améliorer avec quelques scripts en post-chargement qui donneront plus de peps à celle-ci tels que le glisser-déposer et les animations.
le préchargement peut sembler de prime abord l’exact opposé du post-chargement, mais il a en fait un objectif différent. Le préchargement de composants consiste à profiter du temps où le navigateur est inactif pour charger des composants de la demande dont vous aurez besoin plus tard (comme les images, les styles et les scripts). De cette façon, lorsque l’internaute visite la page suivante, vous pourriez avoir la plupart des composants déjà dans le cache et votre page se chargera beaucoup plus rapidement pour l’utilisateur.
Il y a en fait plusieurs types de préchargements :
Une page complexe signifie plus d’octets à télécharger et aussi un accès au DOM plus lent en JavaScript. Cela fait une différence si vous parcourez 500 ou 5000 éléments DOM sur la page lorsque vous souhaitez ajouter un gestionnaire d’événements par exemple.
Un nombre élevé d’éléments DOM peut être un symptôme qu’il ya quelque chose qui devrait être amélioré avec le balisage de la page sans nécessairement supprimer du contenu. Utilisez-vous des tableaux imbriqués à des fins de mise en page ? Ajoutez-vous plus de balises <div>
seulement pour résoudre des problèmes de mise en page ? Peut-être y a t’il une meilleure façon, sémantiquement plus correcte, de faire votre balisage.
Les YUI CSS utilitaires sont une aide précieuse pour la mise en page : grids.css peut vous aider pour la disposition générale, fonts.css et reset.css peuvent vous aider à redéfinir la mise en forme par défaut du navigateur. C’est une chance de repartir de zéro et de réfléchir à votre balisage, par exemple utilisez des <div>
seulement quand c’est logique sémantiquement et non parce que la balise rend une nouvelle ligne.
Le nombre d’éléments DOM est facile à tester, il suffit de taper dans la console de Firebug : document.getElementsByTagName('*').length
.
Et combien d’éléments DOM sont trop nombreux ? Vérifiez d’autres pages similaires possédant un bon balisage. Par exemple, Yahoo! est une page très chargée et reste pourtant sous la barre des 700 éléments (balises HTML).
La répartition des composant permet de maximiser le nombre de téléchargements parallèles. Assurez-vous de ne pas utiliser plus de 2-4 domaines en raison du coût de recherche DNS. Par exemple, vous pouvez héberger votre code HTML et du contenu dynamique sur www.example.org
et répartir les composants statiques entre static1.example.org
et static2.example.org
.
Visitez “Maximizing Parallel Downloads in the Carpool Lane” de Tenni Theurer et Patty Chi pour plus d’informations.
Les iframes permettent d’insérer un document HTML dans le document parent. Il est important de comprendre comment fonctionnent les iframes afin de les utiliser efficacement.
Les “pour” :
Les “contre” :
Les requêtes HTTP sont chères alors faire une requête HTTP et obtenir une réponse telle que “404 Not Found” est totalement inutile et va ralentir l’expérience utilisateur.
Certains sites ont des pages 404 utiles “Vouliez-vous dire … ?”, ce qui est excellent pour l’expérience utilisateur mais toujours un gaspillage de ressources serveur (base de données, etc.). C’est particulièrement mauvais quand le lien a un JavaScript externe est faux et que le résultat est un 404. Tout d’abord, ce téléchargement va bloquer les téléchargements parallèles. De plus, le navigateur peut essayer d’analyser le corps de la réponse 404 comme s’il s’agissait du code JavaScript et en essayant de trouver quelque chose d’utilisable en elle.
L’accès aux éléments du DOM avec JavaScript est lent. Afin d’avoir une page plus réactive, vous devez :
Consultez le YUI theatre’s “High Performance Ajax Applications” de Julien Lecomte pour plus d’informations.
Parfois, les pages sont perçues comme moins réactives à cause des trop nombreux gestionnaires d’événements attachés aux différents éléments de l’arborescence DOM et qui sont ensuite exécutés trop souvent. C’est pourquoi l’utilisation d’une délégation d’événement est une bonne approche. Si vous avez 10 boutons à l’intérieur d’un div
, attachez un seul gestionnaire d’événements sur le div au lieu d’un gestionnaire pour chaque bouton. Les événements vont survenir afin que vous soyez en mesure de capturer l’événement et déterminer de quel bouton il provient.
Vous n’avez pas besoin d’attendre l’événement onload pour commencer à faire quelque chose avec l’arborescence DOM. Souvent, tout ce dont vous avez besoin est que l’élément auquel vous souhaitez accéder soit disponible dans l’arbre. Vous n’avez pas à attendre que toutes les images soient téléchargées. DOMContentLoaded
est l’événement que vous pouvez envisager d’utiliser à la place de onload, mais jusqu’à ce qu’il soit disponible dans tous les navigateurs, vous pouvez utiliser l’utilitaire YUI Event qui a une méthode onAvailable
.
Consultez le YUI theatre’s “High Performance Ajax Applications” de Julien Lecomte pour plus d’informations.
Une des meilleures pratiques précédentes indique que les feuilles de style CSS devraient être chargées en haut de page afin de permettre le rendu progressif.
Dans IE @import
se comporte comme utilisant <link>
en bas de page, il est donc préférable de ne pas l’utiliser.
Le filtre propriétaire IE AlphaImageLoader
vise à régler un problème de couleurs vraies semi-transparentes dans les versions IE < 7. Le problème avec ce filtre est qu’il bloque le rendu. Le navigateur se bloque lorsque l’image est en cours de téléchargement. Il augmente également la consommation de mémoire et est appliqué par élément, et non par image, de sorte que le problème est multiplié.
La meilleure approche est d’éviter complètement AlphaImageLoader
et d’utiliser une version dégradée PNG8 à la place dans IE. Si vous avez absolument besoin du filtre AlphaImageLoader
, utilisez le vieux hack _filter
afin de ne pas pénaliser vos utilisateurs IE7+.
Quand le designer en a fini avec la création des images pour votre page web, il y a encore certaines choses que vous pouvez essayer de faire avant d’envoyer les images sur votre serveur web.
identifier-verbose image.gif
Lorsque vous voyez une image de 4 couleurs utilisant une palette de 256 couleurs, il y a matière à amélioration.imagemagick
donne un PNG totalement sûr à utiliser: convert image.gif image.png
“Tout ce que nous disons, c’est : donnez une chance au PiNG !”pngcrush image.png -rem alla -reduce -brute result.png
jpegtran
sur tous vos fichiers JPEG. Cet outil effectue des opérations JPEG sans perte comme la rotation et peut également être utilisé pour optimiser et supprimer commentaires et autres informations inutiles (telles que les informations EXIF) de vos images. jpegtran -copy none -optimize -perfect src.jpg dest.jpg
.Ne pas utiliser une image plus grande que votre besoin sous prétexte de pouvoir définir la largeur et la hauteur en HTML. Si vous avez besoin <img width="100" height="100" src="mycat.jpg" alt="My Cat" />
alors votre image (Mycat.jpg) devrait faire 100x100px plutôt qu’utiliser une image de 500x500px réduite.
Le favicon.ico est une image qui réside à la racine de votre serveur. C’est un mal nécessaire parce que même si vous ne vous en souciez pas, un navigateur saura toujours le demander. Il est donc préférable de ne pas répondre avec un 404 Not Found
. En outre, puisqu’il est sur le même serveur, des cookies sont envoyés chaque fois qu’il est demandé. Cette image interfère également avec la séquence de téléchargement. Par exemple dans IE, lorsque vous demandez des composants supplémentaires dans le onload, le favicon sera téléchargé avant ces composants supplémentaires.
Donc, pour pallier les inconvénients d’avoir un favicon.ico, assurez-vous de :
ImageMagick peut vous aider à créer de petits favicons.
Cette restriction est liée au fait que l’iPhone ne mettra pas en cache de composants plus grands que 25K. Notez que c’est la taille non compressée. C’est là où la minification est importante parce que gzip seul peut ne pas suffire.
Pour plus d’informations, consultez “Performance Research, Part 5: iPhone Cacheability - Making it Stick” par Wayne Shea et Tenni Theurer.
Rassembler les composants dans un document multi-parties (comme un e-mail avec des pièces jointes), permet de récupérer plusieurs composants en une seule requête HTTP (souvenez-vous : les requêtes HTTP sont coûteuses). Lorsque vous utilisez cette technique, vérifiez d’abord si le terminal utilisateur la prend en charge (Ceci ne fonctionne pas sur iPhone).
Il est plus fréquent qu’on ne le croit de trouver des images avec un attribut src vide. Ceci apparaît sous deux formes :
<img src="">
var img = new Image();
img.src = "";
Les deux formes ont le même effet : le navigateur fait une nouvelle demande à votre serveur.
Pourquoi est-ce un mauvais comportement ?
La cause de ce comportement est la façon dont la résolution URI est effectuée dans les navigateurs. Ce comportement est défini dans la RFC 3986 - Uniform Resource Identifiers. Quand une chaîne vide est rencontrée comme URI, celle-ci est considérée comme un URI relatif et est résolu selon l’algorithme défini dans la section 5.2. Ce cas particulier est détaillé à la section 5.4. Firefox, Safari et Chrome ont tous résolu ce problème de chaîne vide correctement dans leur cahier des charges, alors qu’Internet Explorer le résout de façon incorrecte, apparemment selon une version antérieure de la spécification, RFC 2396 - Uniform Resource Identifiers (rendue obsolète par la RFC 3986). Ainsi, techniquement, les navigateurs font ce qu’ils sont censés faire pour résoudre les URI relatifs. Le problème est que dans ce contexte, la chaîne vide est clairement involontaire.
À la section 4.8.2, HTML5 ajoute à la description de l’attribut src de la balise l’indication aux navigateurs de ne pas faire une demande supplémentaire :
L’attribut src doit être présent et doit contenir une URL valide de référencement d’une image, éventuellement animée mais non-interactive, ni paginée ou scriptée. Si l’URI de base de l’élément est la même que l’adresse du document, alors la valeur de l’attribut src ne peut pas être une chaîne vide.
Heureusement, les navigateurs n’auront plus ce problème à l’avenir. Malheureusement, il n’existe pas de telle clause pour <script src="">
et <link href="">
. Peut-être qu’il est encore temps de faire cet ajustement pour s’assurer que les navigateurs n’implémentent ce comportement par accident.
Cette règle a été inspirée par le gourou JavaScript Yahoo! Nicolas C. Zakas. Pour plus d’informations, consultez son article “Empty image src can destroy your site”.