5151 sujets

Le Bar du forum

En parcourant un article sur les meilleur addins pour Firefox je suis tombé sur un très interessant se nommant "YSlow".

Ce composant vient se greffer sur un composant que tout le monde connais ici, j'entend par la "Firebug".

YSlow s'installe donc après Firebug.

Ce composant est developpé par Yahoo.

Voici un petit lien : http://developer.yahoo.com/yslow/

Mais que fais cette outil incroyable ?

Il permet de tester un total de 13 règles simples en relation avec la vitesse des sites web.

En complément à ca, celui-ci donne une liste de "Best Practices" pour la programmations Web ( http://developer.yahoo.com/performance/rules.html ) afin d'optimiser vos perfs !

Je vous laisse le découvrir.



Smiley cligne
Modifié par Cocci_uk (05 Dec 2008 - 17:14)
Notons que certaines règles ne sont pas absolues et peuvent être contestées... ou tout simplement pas pertinentes dans un cadre donné. C'est le cas par exemple de la règle «Placer les appels de scripts externes en fin de code», à relativiser.

Pour ceux qui s'intéressent à la performance des sites web côté client (donc tout le monde ici Smiley cligne ), en français il y a:
http://performance.survol.fr/ (par Éric Daspet)
Modifié par Florent V. (05 Dec 2008 - 21:39)
Oui, mettre les scripts en fin de page c'est bien gentil, mais ce n'est pas toujours réalisable et si vous compressez vos fichiers et/ou utilisez un CDN pour ces fichiers cela suffit en général largement.

(D'ailleurs, à défaut de CDN pour l'ensemble des fichiers statiques, ce qui est quand même assez difficile à mettre en place pour de "petits" sites, vous pouvez quand même alléger le trafic de votre site en linkant directement vos bibliothèques préférées -jQuery, Prototype, Mootools, etc- depuis Google Code)

De plus, les conseils sur le post-load des images si elles ne sont pas dans la fenetre de vision du navigateur, c'est sympa aussi mais c'est quand même très dur à mettre en place automatiquement.

Il y a des "bonnes pratiques" qui sont très simples à mettre en place (je pense à la configuration serveur : gzip et cache) qui aident déjà beaucoup. Après si vous avez moyen de l'automatiser la compression/aggregation des CSS, JS et même des sprites réduit drastiquement le nombre de requetes HTTP.

A Paris Web, il avait été fait mention de l'impact des "refreshs" du DOM des navigateur sur la vitesse de leur affichage, de l'ordre dans lequel écrire ses déclarations CSS pour éviter de demander trop de refresh. Quelqu'un à des infos à ce sujet ?

Edit : Ok, je me tais, il suffisait de suivre le lien de Florent ^^
Modifié par Tymlis (06 Dec 2008 - 18:15)
Finalement je viens de lire les articles et je n'en ressors pas grand chose de significatif. C'est certes interessant de savoir tout ça mais ça a vraiment peu d'impact en situation réelle. Seulement lorsqu'on a 20.000 eléments avec des classes différentes alors peut-on voir un ralentissement de 200ms... mouais...

D'autant plus que là aussi, la façon de gérer la selection est différente par browser...

Bref, ce n'était pas vraiment le genre d'information que je recherchais mais plutot sur les reflows (j'ai utilisé le terme refresh dans mon post précédent, je pense que reflow est le terme que je voulais utiliser) et leur impact sur la vitesse de rendu.
Tymlis a écrit :


Bref, ce n'était pas vraiment le genre d'information que je recherchais mais plutot sur les reflows (j'ai utilisé le terme refresh dans mon post précédent, je pense que reflow est le terme que je voulais utiliser) et leur impact sur la vitesse de rendu.


Je crois que Daniel Glazman a parlé d'un outil pour mesurer le reflow qu'il voulait réaliser (je crois qu'il en a aussi parlé sur Twiter), et puis plus rien.
Oui voila, il est intervenu après la présentation d'Eric Daspet et Nicole Sullivan pour dire "Mais pourquoi vous avez pas parlé des reflows, c'est super important !" et puis... plus rien.

Je vous fais signe si je trouve quelque chose sur le sujet.
Pour les reflows voici un des trucs qui joue : http://performance.survol.fr/2008/11/questions-de-rendu/

Pour les scripts en bas je me permet de désapprouver. L'influence est très importante, surtout avec vos bibliothèques js genre jquery.
Certes on ne peut pas toujours tout placer en bas, mais en général on peut. Les autres cas sont l'exception. Et autant être clair : non, vous n'êtes probablement pas dans l'exception, même si vous le pensez. (tout le monde pense toujours que son propre cas est différent, mais tout le monde ne peut pas être dans l'exception, elle doit par définition être exceptionnelle).

En général mettre une classe .js permet de déclencher un rendu propre dès le départ. Que les comportements js soient déclenchés plus tard, ce n'est généralement pas un problème. Voir entre autres http://performance.survol.fr/2008/04/attendre-le-javascript/ et http://performance.survol.fr/2008/08/styles-avec-et-sans-javascript/

Pour les sélecteurs CSS oui, l'influence n'est pas primordiale. Mais par contre là aussi je me porte en faux. Le fonctionnement des différents navigateurs est similaire : on indexe tags, identifiants et classes, on lit le sélecteur de droite à gauche, c'est le critère le plus à droite qui va déterminer le nombre de noeuds sélectionnés pour vérifier le sélecteur. (je ne donne pas de liens sinon on va croire que je spamme pour mon blog Smiley cligne
Hello et merci de tes réponses Smiley smile

J'avais déjà jeté un oeil à la video des Techs Talks mais je l'avais trouvé bien trop lente pour y accrocher, j'avais juste lu le résumé sur ton blog.

Mettre les JS en bas de page, j'avoue que ça ne me paraissait pas faisable car j'utilise beaucoup le $(document).ready(function() { }); de jQuery et pour moi il se loade une fois que le DOM est complétement disponible, mais avant que les ressources externes soient chargées (à la différence du body.onload). J'avais donc peur qu'en mettant mes javascript en fin de page, certains scripts ne soient pas chargés lorsque .ready() se lance...
Cela dit je n'ai pas réellement testé et je vais essayer tes conseils pour voir si cela se passe sans accrocs Smiley smile

Pour les problèmes de rendus sans JS, j'utilise une méthode alternative proche de la tienne, je fais dans mon <head> un document.write d'un appel vers une feuille de style propre à Javascript dans laquelle mes règles overrident les régles précédentes (sans JS). Comme j'ai vraiment pas mal de CSS supplémentaire en cas de JS activé (Shadowbox, tinyMCE et divers plugins), cela économise vraiment à mes utilisateurs sans JS de télécharger des données inutiles.

Pour finir sur l'importance des selecteurs CSS, tu écris que :
Smiley cite Il faut tenter d’être le plus précis possible à droite, dans l’idéal un identifiant, un nom de classe, ou au moins un nom de balise. Le reste du sélecteur a plus tendance à ralentir qu’autre chose.[/cite]

Tu veux dire qu'être très précis dans les règles ralentit plus qu'autre chose ? Et qu'on peut donc enlever les selecteur inutiles ?
Prenons l'exemple suivant :


<div class="monDiv">
	<ul class="maListe">
		<li>Bla</li>
		<li>Blu</li>
		<li>Blo</li>
	</ul>
	<ul class="maListe">
		<li>Bla</li>
		<li>Blu</li>
		<li class="monItem">Je veux cibler cet élément</li>
	</ul>
</body>

Quelle est la syntaxe la plus performante pour cibler le dernier <li> ?
- div.monDiv ul.maListe li.monItem ?
- ul li.monItem ?
- li.monItem ?
- .monItem ?
- .maListe li.monItem ?
- Encore autre chose ?


Pour le dernier point, de faire les lectures/écritures de propriétés par JS en deux temps c'est très interessant car c'est complétement contre-intuitif. Les élément subissent un reflow lorsqu'on lit leurs propriétés, et non pas quand on leur écrit (ils en subissent un, mais plus tard, il n'est pas immédiat). Je tacherai de m'en souvenir et de suivre ce conseil dans mes développement futurs. Cela dit, j'avais vu une vidéo de quelqu'un de chez Firefox qui expliquait tout cela mais qui mettait bien en garde de faire attention car ce qu'il montrait était comment FF gérait les sélecteurs et il n'avait aucune idée de comment les autres faisaient.
Modifié par Tymlis (10 Dec 2008 - 23:58)
Hello,

Ca m'intéresse aussi car je détail à fond mes css.
De plus, et Edas ne me contredira pas je pense Smiley cligne , le critère etags dans Yslow est à prendre avec parcimonie car la Yahoo team demande tout simplement de les supprimer.
Ce cas doit être utilisé uniquement si le site est hébergé sur différents serveurs.

Attention aussi avec OVH, le fait d'utiliser des variables serveurs suppriment automatiquement le last-modified et l'etag de votre ressource dans la reponse HTTP.

Voilou pour la parenthèse Smiley cligne
Oui, simplement ".monItem" sera plus rapide. Après je ne te dis pas de ne faire que ça. La différence de performance est à contrebalancer avec la souplesse de se servir de la hiérarchie pour les sélecteur. Mais globalement oui : ajouter des choses va donner plus de travail au moteur. C'est vrai sur Webkit et Firefox, très très probablement aussi sous IE et Opera (en tout cas c'est ce que des tests empiriques montrent, et c'est logique car c'est la meilleure manière d'optimiser une analyse CSS / DOM).

Pour la lecture js c'est juste que si tu lis une taille, il faut que l'élément soit déjà dans le rendu et que tout le rendu soit fait. En général le navigateur peut avoir des éléments présents dans le DOM mais pas encore dans le rendu, ou avec un changement en attente qui n'a pas été tracé. En lisant une info dépendante du rendu tu forces le navigateur à lancer tout le rendu en attente pour pouvoir te donner la réponse la plus "vrai" possible.

S'il n'y a rien en attente aucun rendu n'est déclenché. Mais si tu fais dans une boucle lecture/écriture, alors tu sais qu'à chaque lecture tu avais une écriture juste avant (l'itération précédente) et donc qu'à chaque itération tu forces un rendu.
Parce que le rendu se fait globalement sur la page et pas par élément ?

Si je fais une boucle pour lister tous les liens de ma page et que je leur rajoute à chacun 10px de padding, je vais forcer autant de reflow de ma page que de liens dans ma page ?

Avec un exemple trivial (jQuery)

$('a').each(function() {
	$(this).css('paddingTop', $(this).css('paddingTop')+10);
});

Cela veut dire qu'à chaque itération je vais lire le padding-top de mon lien, donc forcer le reflow, puis modifier ce même padding-top. Donc lancer une "mise en attente" du reflow, mais à l'itération suivante je vais activer ce reflow mis en attente pour reprendre l'information, c'est bien ça ?
Je pensais que puisque mon ordre ciblait un élément différent, seul celui-ci était re-rendu (forcant donc effectivement un reflow des éléments un par un)

La "bonne pratique" pour faire ce que j'évoque serait donc plutot :

$('a').each(function() {
	$(this).data('newPaddingTop', $(this).css('paddingTop')+10);
});
$('a').each(function() {
	$(this).css('paddingTop', $(this).data('newPaddingTop');
});

Je passe d'abord en revue mes liens, en stockant leur nouvelle valeur de padding dans une variable interne (ne demandant à priori pas de reflow, donc). A la premiere itération, un reflow est demandé pour trouver le paddingTop. A la seconde itération, pas de reflow, il a déjà été fait et rien entre temps n'est censé avoir mis de reflow en attente.
Pour la deuxieme boucle, chaque attribution du nouveau paddingTop entraine une demande de "mise en attente" du reflow, qui sera effectivement executé, une fois, quand la navigateur aura le temps.

Donc on passe de x reflows (x égal au nombre de liens), à deux avec cette syntaxe.

Est-ce que j'ai bien compris ce que tu disais, ou me suis-je fourvoyé ?
Sauf erreur de ma part oui, le reflow implique la page, pas l'élément. Je suppose qu'ils ont moyen de ne retracer que ce qui touche aux modifications, mais le rendu de la page est quelque chose de complexe et il y a plein de choses interdépendantes partout. Disons qu'il va au moins tracer le rendu de tout ce qui a des changements en attente, pas que de ce que tu souhaites.

Pour ton petit code, je dirai que oui, la seconde solution serait meilleure ... Toutefois je ne connais pas le fonctionnement de "data()". Si ça fait ce que je pense, c'est à dire stocker la donnée directement dans le DOM, c'est très lent comme système.

Un bon tableau temporaire fera je pense mieux l'affaire (à tester) :


var tab = [] ;
$('a').each(function() {
        var el = $(this) ;
	tab.push( [ el , el.css('paddingTop') ];
});
for(var j = tab.length -1 ; tab >= 0 ; j--) {
  tab[j][0].css('paddingTop', tab[j][1]) ; 
}


Note 1: je ne connais pas jquery, il y a peut être plus joli pour passer dans la seconde boucle, mais le principe est là. J'ai aussi mis $(this) en cache car si la fonction $ fait ce que je pense, ça peut être assez lourd (mais je peux me tromper là aussi).

Note 2: cette séparation en deux boucles est ce que je comprend de la vidéo de David Barron, ça mérite des test, que je n'ai pas encore fait pour tous les cas que je voudrai tester.
Modifié par Edas (12 Dec 2008 - 12:33)
Merci de ta réponse Smiley smile

Je partais du postulat que les requetes de jQuery sont optimisées, comprendre par là que plusieurs appels à $(this) ne vont pas créer des instances à chaque fois, mais plutot faire fonctionner un système de références qui pointent vers l'élément jQuery (abrégé par $). Donc que faire appel à $(this).method() serait très rapide, vu qu'il fait appel à jquery.method(this).

Idem pour .data(), j'imagine que cela fonctionne avec un tableau interne à l'élément jQuery plutot que de le stocker dans le DOM.

Mais ce ne sont que des suppositions, j'ai peut-être un peu trop confiance en la puissance de jQuery, je vais vérifier dans le code ce qu'il en est.

Edit :
Je confirme que jQuery garde un cache des éléments en interne. Je n'ai pas cherché assez profond pour savoir l'impact que cela avait sur les selecteurs, en tout cas pour .data() il est clair qu'il n'enregistre pas les informations dans le DOM mais dans ce cache interne.
Faites un dump de jQuery.cache pour voir si vous voulez.

Pour les selecteurs $() par contre, je ne suis pas sur d'avoir suffisamment compris le code pour savoir exactement comment cela fonctionne. Cela retourne un object (initialisé par jQuery.fn.init()) qui n'est autre que le, ou le tableau des, élément(s) matché(s) par la requete.
$(this) retourne this en tout cas.
Après comment les différents appels aux méthodes sont gérés, j'avoue que je m'y perds un peu. $(this).method() fait bien appel à jQuery.fn.method(this) comme je l'imaginais, mais comment la liaison entre les deux fonctionne (nouvelle instance ? l'objet généré hérite de toutes les méthodes de jQuery ?) là j'avoue ne pas savoir.
Modifié par Tymlis (12 Dec 2008 - 15:48)
Du coup j'ai regardé un peu jquery pour voir.


Si c'est la première fois que tu utilises data() pour cet élément, il va bien créer un objet dans le DOM de ton élément.
En fait la première fois que tu utilises data() sur un élément, il créerait un expando du nom de jquery-un-identifiant-compliqué avec un entier à l'intérieur. Ensuite ils peuvent stocker tes attributs dans une table de correspondance en utilisant cet entier comme index.

C'est une bonne idée, qui évite pas mal des problèmes de mem leak que peuvent avoir les expandos.
À leur décharge, ils n'ont pas vraiment le choix. La seule alternative que je vois serait d'attribuer un identifiant unique à chaque noeud la première fois qu'on l'utilise, mais il y aurait des effets de bord.
Par contre du coup si tu parcours 1000 liens, ça revient quand même à faire 1000 expandos ce qui est potentiellement lent ou pas top sur certains navigateurs.


Pour $this ça demande du boulot de fouiller, donc lachement je laisse l'exercice au lecteur. Disons que au mieux on passe dans une condition qui remarque que c'est un élément déjà étendu par jquery, et la fonction ne fait rien (et mon code ne fait que éviter un appel inutile). Dans le cas contraire c'est un peu plus utile. De ce que j'en ai vu Jquery a l'air assez smart, donc ils gèrent peut être déjà ça bien et mon cache de $this est discutable.
Modifié par Edas (12 Dec 2008 - 17:31)
Quelques infos supplémentaires sur jQuery et les optimisations de performance :
http://www.tvidesign.co.uk/blog/improve-your-jquery-25-excellent-tips.aspx

L'exemple 7 me parait un brin bancal, si .myList n'est pas à la base un <ul>, je comprends bien que lui ajouter des <li> prenne du temps...
Et pour l'exemple 8, il aurait été sans doute bien plus rapide de selectionner $('ul.myList li.listItem'+i) plutot que $('.listItem'+i).
Certes, cela aurait été plus lent qu'une recherche par #id, mais ça aurait déjà donné des résultats plus interessants

Par contre il remplit un peu ses 25 tips par donner une idée, puis en la rectifiant pour faire mieux après, ce qui réduit en fait le nombre de bonnes idée (je pense aux points 13/14).
(L'exemple 16 me parait ridicule par contre)

En tout cas, cet article m'a fait découvrir le timer de Firebug que je ne connaissais pas oO et qui va m'aider à faire quelques tests de performance en tout cas.