23
Eviter que les pixels « bavent » sous Illustrator
Bon, ça fait longtemps que je n’ai rien mis sur ce blog. Désolé, beaucoup de boulot, tout ça, tout ça… D’ailleurs, je parts en vacances donc ça ne va pas s’arranger dans l’immédiat.
MAIS ! Je ne parts pas sans une petite compensation…
Voilà un tutoriel Adobe qui explique comment éviter que les pixels de vos créations Illustrator exportées en bitmap ne « bavent ». En gros sous Illustrator CS4 c’est très chiant et sous CS5 il y a une fonction magique pour ça…
11
Petit-déj Globalis à propos de l’optimisation FrontEnd
Jeudi dernier la société Globalis organisait un petit déjeuner intitulé “Contexte et enjeux de l’optimisation FrontEnd” auquel j’ai participé.
L’optimisation FrontEnd est ici comprise essentiellement comme un moyen d’accélérer la vitesse de chargement d’une page Web, d’un point de vue technique (temps de chargement global), mais aussi du point de vue de la vitesse ressentie par l’utilisateur (attente avant début d’affichage des premiers éléments par exemple).
Je vous propose un petit résumé de cette conférence agrémenté de quelques remarques personnelles.
Les gens de Globalis ont eu la sympathie de m’envoyer le fichier PDF de leur présentation et m’ont autorisé à le publier sur ce blog : Présentation Globalis : Contexte et enjeux de l’optimisation FrontEnd
Présentation du contexte
La société Globalis :
- 13 ans d’expérience
- Spécialiste PHP
- 25 experts
- Créateur de PHPIndex
Pourquoi optimiser la vitesse des pages ?
- Améliorer l’agrément et la qualité perçus par l’utilisateur
- Le temps d’affichage des pages est pris en compte par Google pour le référencement
- L’aspect Green IT
Remarque : c’est bien la première fois que j’entend parler du Green IT comme argument pour optimiser la vitesse d’affichage des pages. Perso, je pense qu’on est très très loin de voir des entreprises se soucier de l’environnement dans la mise en œuvre d’un site Web !
Remarque 2 : JP vient de me faire remarquer un article de Search Engine Land qui confirme et détail la prise en compte de la vitesse des pages dans le rankink de Google. Vous pouvez aussi voir la déclaration officielle sur le blog de Google.
Où l’effort doit-il porter ?
- Si les pages d’un site ne s’affichent pas assez vite, il est tentant pour certains décideurs de se contenter de “mettre de plus gros serveurs”. Plus concrètement, essayer d’optimiser les ressources hardware qui, par nature, sont très facilement interchangeables.
- Concrètement ce n’est pas forcément la meilleure solution pour un site. Surtout que les problèmes de lenteur proviennent plus souvent aujourd’hui du client (ordinateur, connexion et navigateur de l’internaute) que du serveur (le serveur n’envoi pas immédiatement les données car ils prend du temps à les calculer ou à les récupérer dans une base…)
- Il est donc souvent plus efficace de revenir sur la conception d’un site que d’essayer de gagner quelques millisecondes sur le temps de réponse du serveur.
Remarque : je suis assez d’accord avec ce constat, même si ça n’empêche pas certaines applications Web d’être mal développées côté serveur et de répondre plus lentement qu’elle ne devraient (code qui n’optimise pas ses requêtes SQL par exemple). On ne parle pas non plus, bien entendu, des sites sous-dimensionnés (trop de visiteurs simultanés) victimes de leur succès…
Quelques chiffres éloquents
Les pages sont en moyenne 10 fois plus lourdes aujourd’hui qu’il y a 15 ans !
- 1995 : 25 ko / 5 ressources appelées dans la pages (images, feuilles de styles…)
- 2010 : 250 ko / 42 ressources
79% des internautes considèrent qu’une page est trop lente si elle prend plus de 2 secondes à charger.
Google Maps a amélioré sa vitesse d’affichage de 30% en passant de 100 ko à 70 ko.
On considère qu’on a un gain significatif à partir de 100ms. C’est le temps minimum de chargement de chaque requête faite par le navigateur, quelque soit le type et le poids de la ressource chargée ! Il est facile de comprendre alors que le premier conseil d’optimisation est de diminuer au maximum le nombre de requêtes…
Conseils d’optimisation
3 grands axes d’optimisations :
- Diminuer le nombre de requêtes
- Alléger le poids des ressources
- Améliorer la vitesse ressentie par l’utilisateur
Diminuer le nombre de requêtes
- Paramétrer le cache des ressources statiques pour qu’elle ne soit pas rechargées par le navigateur inutilement
- Utiliser la technique des sprites en CSS pour factoriser les images
- Packager les ressources => regrouper les fichiers CSS en un et les fichiers JS en un. Il y a des outils pour automatiser ça
Alléger le poids des ressources
- Utiliser la compression GZip (via un module Apache ou un paramétrage PHP) peut réduire jusqu’à 80% la taille d’une ressource statique
- Utiliser le format adapté pour les images (plutôt JPG pour les photos, PNG pour les graphismes avec des aplats…)
- Packager les ressources => les outils de packging permettent aussi de “minifier” (ou compacter) les fichiers pour en réduire la taille (la librairie javascript jQuery passe de 155 ko à 25 ko en version packagée…)
- Diminuer la taille des cookies (je suis très dubitatif là-dessus)
- Eviter d’utiliser des cookies pour les ressources statiques (une solution pour cela est d’utiliser un sous-domaine spécifique qui n’utilise pas de cookies pour appeler ces ressources)
Améliorer la vitesse ressentie par l’utilisateur
En gros, ça revient à démarrer l’affichage de la page le plus vite possible.
- Afficher la page dès le début du parsing HTML (peut de gens s’en souviennent, mais c’était une des raisons “pratiques” de ne pas utiliser les tableaux HTML pour la mise en forme d’une page, car certains navigateurs n’affichent pas un tableau tant que son contenu n’a pas été chargé complètement…)
- Charger les scripts en bas de page (au lieu de la balise head…) (perso je ne trouve pas ça très propre et je préfère les solutions telle que l’injection DOM qui permettent de paralléliser les chargement JS sans affecter celui de la page)
- Ne pas utiliser de “filtres” tels que les fichiers HTC pour Internet Explorer (ils en existe pour gérer les PGN avec couche de transparence alpha par exemple), car ils ralentissent beaucoup l’interface (ils sont notamment recalculés lors d’un simple scroll…)
- Précharger les ressources quand c’est possible (la page d’accueil Google contient par exemple une image utilisée avec la technique des sprites pour des éléments graphiques qui ne sont utilisés que dans les pages de résultats)
- Technique de “post-load” (j’ai pas trop creusé le sujet, il a des plugins pour ça…)
- Paralléliser les téléchargement (notamment pour les images, IE6 par exemple ne sait charger que 2 images à la fois sur un même domaine. Une solution complexe à mettre en œuvre consiste à utiliser 2 ou 3 sous domaines différents pour les images…)
Conclusion
Les deux “experts” qui ont fait l’essentiel de la présentation lors du petit déjeuner semblaient assez jeunes (ce qui n’est pas un défaut en soit ! :p) et étaient très enthousiastes vis à vis de ces techniques d’optimisation…
Ils avaient bien raison, car dans le fond leur propos est juste et le panel de techniques d’optimisation évoqué semble correspondre à l’état l’art aujourd’hui.
Néanmoins je relève qu’ils ont très peut parlé des problèmes de mise en œuvre de ces techniques dans des contextes réel.
Grossièrement, pour avoir une bonne checklist de toutes ces techniques, il suffit d’utiliser le plugin YSlow de Yahoo! (se prononce “Why slow ?”). Il s’agit d’un plugin pour Firebug qui est lui-même un plugin Firefox (véritable “must have” pour les Webdesigner). YSlow analyse la page et lui donne une note basée sur la prise en compte des techniques d’optimisation Frontend recommandés par Yahoo. Ces techniques recoupent en majeur parties celles évoquées lors du petit déjeuner de Globalis.
Le problème aujourd’hui n’est donc pas tellement de savoir quoi optimiser, mais plutôt de savoir le faire efficacement et dans la durée.
Je pense que j’ai pas mal soulé nos hôtes ce matin là en insistant beaucoup par exemple sur la technique des sprites CSS qui est effectivement idéal pour un site dont les images ne bougent pas trop, mais ça devient une autre paire de manche de l’utiliser sur un site mis à jour fréquemment… J’ai eu l’expérience par exemple d’un site où toutes les images au départ étaient des sprites, mais par la suite on s’est mis a ajouter des images petit à petit (un bouton par ci, un visuel par là…). Et comme le travail se fait souvent dans l’urgence, on n’a pas toujours pris la peine d’incorporer la nouvelle image (ou l’image mise à jour) dans la composition de sprite globale. On tend donc à perdre les bonnes habitudes et, en l’occurrence, à augmenter petit à petit le nombre de requêtes par page…
J’en retiens donc qu’il faut bien se pencher sur les techniques automatisables (comme le packaging des fichiers), mais qu’on manque un peu de maturité pour les autres techniques, comme les sprites ou le chargement des images via des sous-domaines distincts.
Et vous ? Utilisez-vous des techniques d’optimisation Frontend ? Si oui lesquelles ? Rencontrez-vous des difficultés ? Connaissez-vous d’autres techniques ?
N’hésitez pas à participer avec vos commentaires !
9
Bien préparer un développement javascript avec jQuery
On me pose souvent des questions sur certaines bonnes-pratiques javascript assez récurrentes en partant de l’utilisation d’une librairie telle que jQuery. C’est pourquoi je profite d’une de ces questions posée récemment (merci Gilles) pour faire un article sur le sujet.
Donc voici les différentes étapes assez systématiques par lesquelles je passe quand je dois entamer un développement javascript dans une page.
1. Charger jQuery
Je passe rapidement sur cette étape, vous pouvez directement l’appeler depuis le serveur de jquery.com ou le copier dans le répertoire local du site.
<script type="text/javascript"
src="http://code.jquery.com/jquery-latest.pack.js"></script>
2. Créer un fichier javascript et le charger dans la page
On ne le redira jamais assez : ne mettez pas de balises <script> dans vos pages ! Préférez un fichier à part du genre monScript.js cela vous permettra de bien séparer les choses. Ici je pars du principe que vous avez un dossier js à la racine du site dans lequel vous mettez ce genre de fichier.
<script type="text/javascript" src="/js/monScript.js"></script>
3. Préparer le fichier javascript
A la longue, mes fichiers javascripts finissent par avoir toujours la même structure… En l’occurence, je commence toujours par écrire les quelques lignes suivantes
(function($){
$(document).ready(function(){
// Méthodes exécutées au chargement du DOM
});
})(jQuery);
a. Englober tout le code dans une fonction anonyme
Ce morceau de code peut déjà sembler complexe à certains, mais une fois décortiqué il se comprend très bien. Dans un premier temps, on crée ce que l’on appelle une fonction anonyme, c’est à dire une fonction qui n’a pas de nom :
function(){ /* contenu de monScript.js */ }
Le but va être de « ranger » tout le code du fichier à l’intérieur de cette fonction. L’avantage est énorme, car grâce au principe de champ d’application des variables et des objets en javascript, tout ce que nous allons faire dans cette fonction ne pourra pas venir perturber d’autres scripts éventuels chargés dans la même page. Par exemple, fini les fonctions avec des noms à rallonge pour être sûr qu’aucune autre n’aura le même nom.
Le problème à ce stade, c’est qu’une fonction (anonyme ou pas) ne s’exécute pas d’elle-même, pour l’exécuter, il faut qu’elle soit appelée. Ainsi, dans le code suivant, la première ligne sert à déclarer la fonction, la deuxième à l’appeler :
var toto = function() { alert("toto") };
// => Pour l'instant rien ne se passe...
toto();
// => Là ma page affiche l'alerte, car la fonction toto est exécutée
La subtilité avec une fonction anonyme c’est justement qu’elle n’a pas de nom ! Mais ce n’est en fait pas un problème, car en javascript vous pouvez considérer que n’importe quel bout de code entre parenthèse est un objet en soit (ou une variable pour faire simple). Du coup, on peut à la fois déclarer une fonction anonyme et l’appeler en mettant la fonction entre parenthèse, puis en juxtaposant des parenthèses vides « () » justes après, comme on le ferait avec le nom d’une fonction classique déclarée auparavant. Ce qui nous donne :
(function(){ /* contenu de monScript.js */ })()
Pour bien comprendre, il faut réaliser que les lignes de codes suivantes sont totalement équivalentes :
function tata() { alert("tata") }; tata();
// => Affiche "tata"
var titi = function() { alert("titi") }; titi();
// => Affiche "titi"
(function() { alert("toto") })();
// => Affiche "toto"
b. Garantir l’équivalence jQuery == $
Au final on profite également du fait de passer par une fonction pour lui passer en paramètre l’objet jQuery. L’intérêt est de profiter du fait que lorsque on déclare une fonction, on déclare au passage le nom des variables internes qui vont correspondre au paramètres passés à la fonction. Par exemple dans le code suivant, je passe la variable globale monMessage à la fonction showMessage à l’intérieur de laquelle je manipule cette fois la variable « m » qui à l’avantage d’être beaucoup plus courte.
function showMessage(m) {
alert(m);
};
var monMessage = "Coucou !";
showMessage(monMessage);
// => Affiche "Coucou !"
Par défaut, jQuery propose déjà une variable « $ » qui est l’équivalent de la variable globale jQuery. Malheureusement on ne peut pas toujours s’y fier, car certaines librairies telles que Prototype ou Mootools peuvent venir surcharger elles aussi la variable $. Il est donc plus sûr de redéfinir cette variable nous-même.
Cette astuce a aussi l’avantage inverse, à savoir permettre d’utiliser jQuery en mode « no conflict ». C’est à dire en lui demandant explicitement de ne pas utiliser la variable $ pour qu’elle soit disponible pour d’autres librairies (et donc éviter les conflits). Car après tout jQuery n’a pas le monopole du dollar !
4. Répartir tout le code dans des fonctions simples avec des noms explicites
Il est souvent tentant de commencer un développement (surtout les plus petits) en écrivant le code sans essayer de le structurer. Mais avec le temps on se rend compte que faire du code propre dès le départ est toujours payant.
Pour cela j’essaie d’appliquer 2 grands principes :
- Je crée des fonctions différentes pour chaque unitée fonctionnelle. Notamment il arrive souvent qu’un développement consiste en deux parties : l’opération en elle même exécutée par le navigateur et le branchement de cette opération sur un évènement (par exemple, le clic sur un lien ou un bouton). Dans ce cas, je sépare ces deux parties en deux fonctions distinctes. L’intérêt principal est bien entendu de pouvoir rappeler la première fonction dans différents cas (on factorise le code). On gagne aussi en clarté et en maintenabilité
- Je nomme mes fonctions de manière explicite en respectant une nomenclature précise. Par exemple une fonction qui est appelée directement au chargement commence par « init », si elle consiste à brancher une fonction sur un évènement elle précise « event » ainsi que l’élément sur lequel elle s’applique, etc.
En bonus, vous pouvez aussi décrire vos fonctions avec des commentaires, ça ne mange pas de pain…
Tout cela nous donne un code qui peut ressembler à celui-ci :
(function($){
// Code exécuté au chargement du DOM
$(document).ready(function(){
initEventOnClickOnSaveButton();
});
/*
* Action lors du clic sur le bouton "sauvegarder"
*/
function initEventOnClickOnSaveButton() {
$("#saveButton").click(function(){
saveFormData($(this).closest("form"));
});
}
/*
* Sérialisation et sauvegarde des données d'un formulaire
* formElement via une requête POST en AJAX
*/
function saveFormData(formElement) {
var $form = $(form), data = $form.serialize();
$.post($form.attr("action"), data);
}
})(jQuery);
5. Utiliser des champs cachés pour transmettre des données aux scripts
Dans l’exemple précédant, je simule une fonction qui doit faire une requête AJAX (via la méthode post de jQuery). Pour déduire l’URL à interroger pour faire la requête, elle utilise la valeur de l’attribut action d’un formulaire. C’est une technique assez pratique et élégante dans la mesure où les formulaires contiennent par nature toutes les informations nécessaires pour envoyer une requête à un serveur, y compris l’URL à interroger. Le problème c’est qu’on a bien sûr pas toujours la possibilité de rencontrer ce cas de figure idéal. Bien souvent, quand on doit faire une requête AJAX, l’URL à interroger doit être générée par une méthode côté serveur (une méthode PHP par exemple).
Pour répondre à cette problématique, un réflexe courant est de générer dynamiquement du code javascript dans la page, comme dans l’exemple suivant :
<script type="text/javascript">
// On suppose qu'il existe déjà une variable PHP $ajaxUrl...
var ajaxUrl = "<?php echo $ajaxUrl ?>";
// => On peut maintenant utiliser la variable globale ajaxUrl en javascript
</script>
Cette façon de faire a plusieurs inconvénients, dont certains vont à l’encontre de quelques unes des bonnes pratiques que nous avons vu juste avant :
- Génération d’une balise <script> dans la page
- Utilisation d’une variable globale qui peut entrer en conflit avec d’autres scripts
La méthode que j’utilise pour pallier à ces problèmes est de passer par des champs cachés. La mise en oeuvre est assez simple. Je commence par mettre le code suivant tout en bas de la page concernée (ou plutôt dans le footer global du site si possible, comme ça les données sont dispo partout) :
<form id="dataForJS">
<input type="hidden" id="ajaxUrl" value="<?php echo $ajaxUrl ?>" />
</form>
Et voilà ! En javascript, il ne me reste plus qu’à aller chercher le champ caché par son identifiant et en récupérer la valeur. Ce qui, avec jQuery, est assez trivial :
var ajaxUrl = $("#ajaxUrl").val();
Vous pouvez même un peu plus automatiser les choses en récupérant directement toutes les URLs s’il y en a plusieurs. En ajouter une revient alors simplement à ajouter un champ caché :
var urls = {};
$("#dataForJs input").each(function(){
var $input = $(this);
urls[$input.attr("id")] = $input.val();
});
alert(urls["ajaxUrl"]);
// => Affiche l'URL stockée dans le champs caché avec l'id "ajaxUrl"
En résumé…
Voilà donc au final à quoi peut ressembler votre fichier JS prêt pour accueillir confortablement vos développements javascript avec jQuery :
(function($){
// Code exécuté au chargement du DOM
$(document).ready(function(){
initUrls();
initEventOnClickOnSaveButton();
});
/*
* Récupération des URLs sérialisées dans la page
*/
var urls = {};
function initUrls() {
$("#dataForJs input").each(function(){
var $input = $(this);
urls[$input.attr("id")] = $input.val();
});
}
/*
* Action lors du clic sur le bouton "sauvegarder"
*/
function initEventOnClickOnSaveButton() {
$("#saveButton").click(function(){
saveFormData($(this).closest("form"));
});
}
/*
* Sérialisation et sauvegarde des données d'un formulaire
* formElement via une requête POST en AJAX
*/
function saveFormData(formElement) {
var $form = $(form), data = $form.serialize();
$.post(urls["ajaxUrl"], data);
}
})(jQuery);
Et vous ? Avez-vous d’autres bonnes pratiques ou des conseils pour préparer vos développements frontend ? N’hésitez pas à les poster en commentaire, je compléterai ou corrigerai l’article au besoin…
Avant propos
WebProdDesign sur Twitter
- Le nouveau Twitter, je l'avais (presque ;-) prédit : http://bit.ly/whytwitterchange 2010-09-16
- Tiens, j'essai un petit ping depuis Seesmic Desktop 2... Sur le papier c'est sympa, mais ça fait un peu lourd pour mon besoin 2010-09-13
- Et aussi 50% de click-back pour les partages via Twitter sur nos sites 2010-08-20
- More updates...
Ma veille techno-marketo…
- Lytro, l’appareil-photo révolutionnaire, est disponible en pré-commande 20 octobre 2011
- The Anatomy of an Effective Homepage 7 octobre 2011
- Un brevet de pico-projecteur interactif déposé par Apple… et Microsoft 8 septembre 2011
- Vacation Relaxation for IT Geeks 19 juillet 2011
- Lazy Load Content and Widgets with MooTools and RequireJS 18 juillet 2011
- SEO : votre newsletter peut affecter le classement de votre site dans Google ! 31 mai 2011
- Le Widget Participants, Gmail a maintenant son module de CRM 27 mai 2011
- The Slow Mo Guys : Fun et caméra à très haute vitesse 25 mai 2011
- minutes.io: un service Web de compte-rendu de réunion 19 mai 2011
- Syncdocs: synchronisez Google Documents avec votre ordinateur [Windows] 4 mai 2011

