Jekyll2017-09-07T08:24:37+00:00/Jean-Pierre Vincent — BrainCrackingPerformances Web HTML5 et JavaScript par Jean-Pierre Vincent, pour BrainCracking
Les manques à gagner de la publicité pour les éditeurs2016-12-21T15:04:21+00:002016-12-21T15:04:21+00:00/2016/12/21/les-manques-a-gagner-de-la-publicite-pour-les-editeurs<blockquote>
<p>Cet article et <a href="/2016/12/21/combien-coute-un-service-tiers-gratuit">le précédent, plus technique, sur les tiers classiques</a> sont une transcription d’une conférence donnée à Blend Web Mix 2016. <a href="https://www.youtube.com/watch?v=7tbTm1Jo0gE">Cliquez ici si vous êtes plus audio que texte</a>.</p>
</blockquote>
<h1 id="la-publicité-et-les-performances">La publicité et les performances</h1>
<p>Mettre “pub” et “perf” dans la même phrase fait rire jaune tout travailleur du Web. Le peu de fois où nous désactivons nos adblockers, nous nous demandons comment font “les gens” pour arriver à naviguer. Le peu de fois où nous testons le site sur autre chose que nos mobiles personnels, généralement performants, nous prenons notre mal en patience car c’est pour le travail. Les vrais utilisateurs eux, sont déjà partis.</p>
<h2 id="performance-perçue-et-performance-business">Performance perçue et Performance business</h2>
<p>Il y a des palanquées de chiffres démontrant la relation business/temps de chargement, regardez ne serait-ce que cette <a href="https://wpostats.com/tags/2016/">collection 2016 chez wpostats</a> (communautaire) ou ces <a href="https://www.dareboost.com/fr/webperf-impacts">traductions en français chez Dareboost</a> (solution commerciale). Laissez moi vous sortir quelques chiffres de chez mes clients.</p>
<p>[caption id=”attachment_1229” align=”alignnone” width=”1091”]<a href="http://braincracking.org/wp-content/uploads/2016/12/onfocus.png"><img class="size-full wp-image-1229" alt="courbe de nombre de pages vues par session pour chaque temps de chargement" src="http://braincracking.org/wp-content/uploads/2016/12/onfocus.png" width="1091" height="509" /></a> une très classique relation engagement / temps de chargement[/caption]</p>
<p>Cette courbe (réalisée avec le produit commercial <a href="http://webperf.io/">webperf.io</a>) est assez classique et range les utilisateurs par temps de chargement (axe horizontal) et indique pour chaque segment le taux d’engagement (plus c’est haut, plus il consomme). Ici c’est un site de presse, donc c’est le nombre de pages consommées par session. Comme souvent, on voit que les utilisateurs sont moins consommateurs lorsque leurs pages se chargent en plus de 5 secondes. Puis au-delà de 20 secondes, la courbe s’aplanit, car ne restent que les fidèles et les motivés. Je précise que j’ai généré cette courbe en filtrant pour n’avoir que les utilisateurs Windows, afin de ne pas être influencé par les mobiles.</p>
<p>Autre constat en fouillant les chiffres :</p>
<blockquote>
<p>Les utilisateurs avec adblock ont un engagement 50% supérieur aux autres</p>
</blockquote>
<p>Quand on sait que la majorité des temps de chargement sont consacrés à la seule publicité, il devient évident que cette source de revenus a un coût qui dépasse de loin ce qui est écrit sur le contrat avec la régie.</p>
<h2 id="comment-louper-85-des-internautes-français">Comment louper 85% des internautes français</h2>
<p>Avec quoi vos utilisateurs mobiles naviguent-ils ? Vous trouverez sans doute dans vos analytics les modèles de smartphones et tablettes. Prenez le top 25 et allez récupérer pour chaque modèle son prix de vente actuel, occasions comprises (surtout pour les iPhones). Chez 3 clients (2 journaux, 1 site de e-commerce) j’obtiens à peu près la même valeur moyenne : 380€.
En voyant ces stats, je me suis longtemps dit que les français étaient plutôt privilégiés par rapport à leurs voisins et pouvaient mettre de beaux budgets dans leurs smartphones (merci Free ?). Jusqu’à ce que je trouve les prix d’achat du marché français des smartphones :</p>
<p>[caption id=”attachment_1232” align=”alignnone” width=”663”]<a href="http://braincracking.org/wp-content/uploads/2016/12/marche-mobile.png"><img class="size-full wp-image-1232" alt="graphique de répartition du prix d'achat des mobiles dans la population française" src="http://braincracking.org/wp-content/uploads/2016/12/marche-mobile.png" width="663" height="633" /></a> prix d’achat des mobiles en 2015[/caption]</p>
<p>L’étude de marché <a href="http://www.challenges.fr/high-tech/les-grandes-tendances-du-marche-des-smartphones-en-france_60272">est faite par GFK</a> pour le 1er semestre 2015 et montre clairement que <strong>la population qui possède des portables de plus de 300€ ne représente que 15% des français</strong> … La médiane est plutôt comprise entre 100 et 150€.
Utilisent-ils le Web ? Oui puisque d’après l‘<a href="http://www.arcep.fr/index.php?id=13365#c94750"><abbr title="Autorité de Régulation des Communications Électroniques et des Postes">ARCEP</abbr></a> 70% des cartes SIM utilisent la 3G. Même en supposant que seuls les possesseurs des smartphones les plus chers achètent de la 3G, cela signifie qu’il n’est pas rare d’avoir des mobiles de 75-100€ accéder au Web.
Pourquoi ne les voyez-vous pas sur vos sites ? Tentez de charger une page de journal avec un mobile ou une tablette à moins de 200€ ! Je pense que les gens accèdent bien au Web mais que l’expérience d’un site chargé en pub est tellement pénible qu’ils restent sur les applications de Facebook ou Google (où la pub est parfaitement sous contrôle d’ailleurs).</p>
<p>Pour résumer, laisser ses pubs faire n’importe quoi, c’est se couper de la majorité du parc mobile français.</p>
<h2>Pub lourdes ? Essayez avec moins!</h2>
<p>Il y a des stratégies intéressantes pour laisser plus d’utilisateurs devant des publicités plus visibles. Elles jouent sur les modes de facturation de la pub (impression ou visibilité garantie), sur la notion de valeur moyenne de l’inventaire et sur la réalité technique qui est que moins on affiche de pubs, plus ça va vite, et plus l’utilisateur consommera de pages, donc d’affichage de pubs.
Prenons par exemple le journal anglais Guardian.
[video width=”2624” height=”1690” webm=”http://braincracking.org/wp-content/uploads/2016/12/Guardian-pub-differee-peu-nombreuses-et-sticky.webm”][/video]
<a href="http://braincracking.org/wp-content/uploads/2016/12/Guardian-pub-differee-peu-nombreuses-et-sticky.mp4">Lien direct vers la vidéo en MP4</a>.
Sur un réseau que j’ai artificiellement ralenti avant de filmer l’écran, on voit les priorités données :</p>
<ul>
<li>on affiche d’abord texte et photo principale, c’est ce qui fait que l’utilisateur va rester</li>
<li>notez qu’un espace blanc en haut de page est déjà prévu pour y afficher une bannière et éviter les désagréables effets de page qui saute</li>
<li>dans la plus pure tradition de l’amélioration progressive, la plupart des liens sont déjà cliquables et on peut interagir avec le menu même si les fonctionnalités JavaScript n’ont pas encore été récupérées</li>
<li>affichage progressif des fonctionnalités secondaires, comme les boutons de partage sur la gauche</li>
<li>enfin, affichage de deux emplacements pub classiques en haut et à droite</li>
<li>chose importante : aucune publicité en dehors du viewport ne commence à se charger, pour ne pas gêner le chargement des ressources plus visibles</li>
<li>au scroll, la pub de droite reste bien visible</li>
</ul>
<p>Du côté de chez Yahoo!, où la performance frontend a été inventée il y a 10 ans (j’y étais !), on peut aussi espérer qu’un soin particulier ait été apporté aux pubs.
[video width=”2820” height=”1518” mp4=”http://braincracking.org/wp-content/uploads/2016/12/scroll-yahoo-et-pub-unique-sur-le-co%CC%82te%CC%81.mp4”][/video]
Sur la Homepage très chargée en informations, Yahoo! joue sur la visibilité d’une position en particulier (à droite) tandis que les autres positions sont remplies par des pubs très légères à gauche (Unicef) et au centre. Elles se confondent d’ailleurs avec le contenu, évitant au passage la <a href="https://en.wikipedia.org/wiki/Banner_blindness">“banner blindness”</a>, c’est à dire le masquage inconscient par l’utilisateur de tout ce qui ressemble à une pub.</p>
<p>En France, nous avons l’exemple de Libération :
[video width=”2820” height=”1598” mp4=”http://braincracking.org/wp-content/uploads/2016/12/liberation-pub-col-droite-en-sticky1.mp4”][/video]
Encore une fois, moins de positions au global (mais ils pourraient faire mieux), une position à droite qui reste très visible, et j’ai l’impression qu’ils affichent surtout des publicités vidéo, qui ont l‘avantage de se vendre plus cher. Ce ne sont pas mes clients (du coup ils n’ont peut être justement pas besoin de moi :) ), donc je ne sais pas si c’est rentable pour eux au final, mais en tout cas l’expérience utilisateur est plus agréable et en allant sur leur Une, l’identité visuelle du site se démarque quelque peu des autres sites de presse.</p>
<p><strong>En résumé</strong> : on réduit l’inventaire, on y met des formats différenciants ou légers, et dans tous les cas on maximise la visibilité d’une position en particulier. Cette dernière étant vendue plus chère, elle compensera les pertes sur les positions disparues qui étaient vendues à l‘impression. L’amélioration de l’expérience utilisateur et l’ouverture à une gamme plus large de mobiles devraient remonter le nombre de pages vues.
Le fait d’avoir moins d’inventaire permet aussi aux équipes de se concentrer sur la qualité de ce qui est diffusé, ce qui est à mon avis un gros plus pour l’image de marque.</p>
<h1>La pub et la qualité</h1>
<h2>L’image de marque</h2>
<p>Une mauvaise publicité ne dégrade-t-elle pas l’image de marque des éditeurs ? Ça semble évident et pourtant je n’ai pas réussi à trouver d’étude sur ce sujet. Les éditeurs historiques devraient protéger leur image de marque, dont la crédibilité est encore un critère qui fait cliquer sur un article par exemple, mais on sent bien qu’ils ont partiellement abandonné la partie !
Rassurez-moi, il n’y a pas que moi qui pense que les services de “content discovery” (en France Outbrain, Ligatus ou Taboola) mettent à mal l’image de sérieux des titres de presse ?</p>
<p>[caption id=”attachment_1233” align=”alignnone” width=”1702”]<a href="http://braincracking.org/wp-content/uploads/2016/12/mauvaises-pubs.png"><img class="size-full wp-image-1233" alt="captures d'écran de pubs sans liens avec le contenu" src="http://braincracking.org/wp-content/uploads/2016/12/mauvaises-pubs.png" width="1702" height="1118" /></a> À gauche : “Le Monde” et Outbrain,<br />À droite “Le Parisien” et Taboola[/caption]</p>
<p>Je n’ai pas eu à me forcer pour trouver sur “Le Monde” sous un article à propos du traité CETA la mise en avant d’articles aux sujets … variés : femme pulpeuse, mal au dos, fabriquer un vélo, Madonna VS Trump … Et pour “Le Parisien”, les thèmes sont petites culottes, photos en bikini et poids des stars, juste sous deux articles à propos du sexisme… Oh Ironie.</p>
<p>Avoir une marque c’est aussi avoir une identité visuelle forte.</p>
<p>[caption id=”attachment_1235” align=”alignnone” width=”1299”]<a href="http://braincracking.org/wp-content/uploads/2016/12/charte.png"><img class="size-full wp-image-1235" alt="Extrait des slides de la conférence de Renaud Forestié sur le redesign des sites de presse" src="http://braincracking.org/wp-content/uploads/2016/12/charte.png" width="1299" height="779" /></a> Extrait des slides de la <a href="https://vimeo.com/136370421">conférence de Renaud Forestié</a> sur le redesign d’un site de presse[/caption]</p>
<p>Pas facile de se démarquer des autres quand il faut respecter la charte des régies : avoir une largeur de 1000 pixels (à 20 près), un contenu forcément centré et un pavé de droite visible dans les 800 premiers pixels. Compliqué également pour ces journaux de tenter des formats nouveaux, ce qui est un manque à gagner quand on sait que l’introduction de nouveaux formats augmente toujours le taux d’interaction pub (<a href="https://www.google.fr/url?sa=t&rct=j&q=&esrc=s&source=web&cd=1&cad=rja&uact=8&ved=0ahUKEwjK_93JuKLOAhXBWBoKHS5CC9IQFggeMAA&url=http%3A%2F%2Fwww.iabfrance.com%2Fsystem%2Ffiles%2Fefficacite_digitale_etude_millward_brown_iab_06.2010v.publique.pdf%3Fdownload%3D1&usg=AFQjCNG93wHhZeW92MXzlwlQQI4WiTGtUw&sig2=k8VvsPw-kKesshK_27gevw">source</a>).</p>
<h2>Qui paye la non-qualité des pubs ?</h2>
<p>Quand on arrive de l’extérieur comme moi, c’est compliqué de comprendre comment les régies et les fabricants de pub peuvent se permettre de diffuser les choses suivantes.</p>
<h3>Des virus !</h3>
<p>[caption id=”attachment_1246” align=”alignnone” width=”712”]<a href="http://braincracking.org/wp-content/uploads/2016/12/msn.png"><img class="size-full wp-image-1246" alt="Titre d’article : MSN diffuse des virus via la publicité" src="http://braincracking.org/wp-content/uploads/2016/12/msn.png" width="712" height="386" /></a> Quand la pub diffuse des virus, c’est l’éditeur qu’on accuse.[/caption]</p>
<p>Régulièrement des pubs vérolées sont diffusées, y compris par <a href="https://blog.malwarebytes.com/threat-analysis/2016/01/msn-home-page-drops-more-malware-via-malvertising/">des grands éditeurs comme Yahoo! ou MSN</a>. Les virus exploitent les failles des navigateurs non patchés pour s’installer automatiquement sur les machines. Ne croyez pas que ça ne concerne que les vieux <abbr title="Internet Explorer">IE</abbr> sur <abbr title="Windows XP">XP</abbr>, iOS 10.0 a récemment été vulnérable à ce même genre d’attaques : <a href="https://support.apple.com/en-us/HT207271">le simple affichage d’un JPEG permettait de l’infecter</a>. Il est donc rentable pour des groupes criminels de pirater des serveurs de pub ou simplement d’acheter des emplacements avec très peu de budget (RTB + ciblage navigateur). Notez que dans ces cas là, c’est l’image de marque du diffuseur qui prend, pas la régie ni ses prestataires.</p>
<h3>Des pubs qui ne marchent pas</h3>
<p>Au hasard de mes audits de WebPerf qui se font sans adblocker (moment toujours pénible), il est fréquent de tomber sur des pubs qui non seulement font ramer le site mais qui en plus ne marchent pas. Comme cette pub qui tourne en boucle sur une erreur, fait plafonner le processeur mais ne réussira jamais à jouer la vidéo.</p>
<p>[caption id=”attachment_1247” align=”alignnone” width=”1494”]<a href="http://braincracking.org/wp-content/uploads/2016/12/erreurs-pub.png"><img class="size-full wp-image-1247" alt="screenshot d’une pub vidéo en erreur" src="http://braincracking.org/wp-content/uploads/2016/12/erreurs-pub.png" width="1494" height="716" /></a> Non, cette pub ne s’affichera jamais, mais on va la facturer quand même.[/caption]</p>
<h3>Des pubs loooooongues</h3>
<p>D’autres fois il faut tellement de temps à une publicité pour s’afficher qu’on se demande comment quelqu’un espère gagner de l’argent grâce à elle.</p>
<p>[caption id=”attachment_1248” align=”alignnone” width=”1350”]<a href="http://braincracking.org/wp-content/uploads/2016/12/vache-qui-rame.png"><img class="size-full wp-image-1248" alt="pub très longue à afficher (17 secondes)" src="http://braincracking.org/wp-content/uploads/2016/12/vache-qui-rame.png" width="1350" height="290" /></a> La Vache Qui Rame™[/caption]</p>
<p>Ici il a fallu 17 secondes à la marque pour s’afficher, sur une connexion tout à fait standard. Sur un article passe encore car il y a de bonnes chances que l’utilisateur soit encore là, mais sur une page type Home, résultat de recherche, il n’y a probablement plus personne pour voir la marque.</p>
<h3>Qui paye pour ces pubs peu ou pas du tout visibles ?</h3>
<p>Il y a deux cas de figure.</p>
<p>L’annonceur paye la régie pour un <strong>volume d’impressions</strong>. Techniquement tout se joue sur le moment où la comptabilisation de l’impression remonte. Dans le cas “Vache qui rit”, il y avait plus de 12 secondes entre la comptabilisation par la régie et l‘apparition de la marque… Dans ces cas, c’est donc <strong>les annonceurs qui sont floués</strong>, mais ils doivent y être habitués puisque cela fait 20 ans que des pubs sont vendues à l’affichage même si aucun humain ne les voit (56% des pubs ne sont pas vues <a href="https://www.thinkwithgoogle.com/infographics/5-factors-of-viewability.html">d’après Google</a>).</p>
<p>L’annonceur achète avec une <strong>garantie de visibilité</strong>. La visibilité est, d’après le groupement des professionnels de la pub (<abbr title="Interactive Advertising Bureau ">IAB</abbr>), lorsqu’au moins 50% des pixels d’une pub sont dans le viewport navigateur pendant plus d’une seconde. Si la comptabilisation se fait honnêtement, c’est à dire après le chargement de la pub mais que celle-ci est trop lente ou en erreur, <strong>c’est l’éditeur qui a un manque à gagner</strong> en ayant fait une page vue non facturable.</p>
<p><strong>La régie gagne à tous les coups…</strong></p>
<video width="540" height="300" src="http://braincracking.org/wp-content/uploads/2016/12/ramasse-tout.mp4" autoplay="autoplay" loop="loop" muted="" playsinline=""><object width="540" height="300" classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"><param name="src" value="http://braincracking.org/wp-includes/js/tinymce/plugins/media/moxieplayer.swf" /><param name="flashvars" value="url=/wp-content/uploads/2016/12/ramasse-tout.mp4&poster=/wp-admin/" /><param name="allowfullscreen" value="true" /><param name="allowscriptaccess" value="true" /><embed width="540" height="300" type="application/x-shockwave-flash" src="http://braincracking.org/wp-includes/js/tinymce/plugins/media/moxieplayer.swf" flashvars="url=/wp-content/uploads/2016/12/ramasse-tout.mp4&poster=/wp-admin/" allowfullscreen="true" allowscriptaccess="true" /></object></video>
<p>Ce ne sont pas de grandes méchantes pour autant : elles sont réactives dès lors qu’on leur signale des publicités en erreur ou qui font planter le site par exemple, et ensuite des rapports de force polis s’installent comme dans toute relation commerciale. Mais disons qu’elles n’ont pas le besoin économique impératif de vérifier toutes les créations de tous leurs prestataires.</p>
<h2>Quelles solutions pour les éditeurs ?</h2>
<p>Le rapport de force étant en faveur des intermédiaires de la pub, qui ont bien d’autres canaux de diffusion, c’est clairement aux éditeurs de s’organiser pour ne pas périr étouffés.
D’abord en définissant des contraintes de qualité technique (côté éditorial, ça ne peut se faire qu’au cas par cas). Ça tombe bien, <abbr title="Groupement des professionnels de la pub américains">l’IAB US</abbr> a défini un <a href="http://www.iab.com/wp-content/uploads/2015/11/IAB_Display_Mobile_Creative_Guidelines_HTML5_2015.pdf">cahier des charges précis</a> où, pour chaque format, ils définissent des limites. Par exemple un Skyscraper ne doit pas dépasser 200Ko au démarrage, puis a le droit de récupérer 300Ko supplémentaires 1 seconde après le chargement de la page. Une vidéo doit se jouer en 24 images/seconde pendant 15 secondes max et un interstitiel animé ne doit pas dépasser 15 secondes.</p>
<p>[caption id=”attachment_1253” align=”alignnone” width=”663”]<a href="http://braincracking.org/wp-content/uploads/2016/12/iab.png"><img class="size-full wp-image-1253" alt="De la contrainte nait la créativité … et une meilleure rentabilité" src="http://braincracking.org/wp-content/uploads/2016/12/iab.png" width="663" height="673" /></a> De la contrainte nait la créativité … et une meilleure rentabilité[/caption]</p>
<p>C’est dommage que les pros français de la pub n’en aient jamais entendu parler, mais encore une fois ce n’est pas l’univers bouillonnant des intermédiaires de la pub qui va s’auto-imposer des contraintes tant que leurs clients ne leur demande rien.
On peut aussi définir un seuil de temps processeur à ne pas dépasser et exiger le respect des actions techniques de base (compression, concaténation, mise en cache …). Que l’on utilise ces guidelines ou que chaque site les adapte à sa cible (typiquement, nos réseaux mobiles nous permettent d’être moins stricts qu’aux US), le tout est d’en avoir et de s’accorder dessus avec sa régie.</p>
<p>Une fois les règles définies, il faut passer à la phase de vérification, si possible automatisée donc permanente et systématique.
Je n’ai pas eu connaissance de solution, même commerciale, qui permettrait de vérifier automatiquement les pubs délivrées à partir d’un cahier des charges. Même pour surveiller les tiers de manière générale il n’y a pas vraiment d’offre complète, à tel point que je suis en train de construire ce genre de dashboard sur commande.</p>
<p>[caption id=”attachment_1255” align=”alignnone” width=”711”]<a href="http://braincracking.org/wp-content/uploads/2016/12/stop-pub.png"><img class="size-full wp-image-1255" alt="Visualisation du poids servi par nom de domaine" src="http://braincracking.org/wp-content/uploads/2016/12/stop-pub.png" width="711" height="241" /></a> Visualisation du poids servi par nom de domaine. Tiens une pub de 3 Mo a arrêté d’être distribuée.[/caption]</p>
<p>On peut surveiller la consommation processeur, réseau, les temps de réponse des tiers… Pour certaines campagnes ciblées on pourrait récupérer les métriques de visibilité : savoir quand le logo de la marque est visible par exemple, vu depuis les postes utilisateur, information que certaines régies font payer. Lorsque le business model dépend des tiers, ce monitoring peut expliquer la variation de certaines métriques métier comme la baisse du nombre de vues le même jour qu’une défaillance partielle des serveurs de la régie.
À la fin, cela redonne matière à négocier un peu plus de qualité ou peut redonner confiance envers votre prestataire.</p>
<h1>Conclusion</h1>
<p>Nous avons vu <a href="http://braincracking.org/2016/12/21/combien-coute-un-service-tiers-gratuit/">dans un précédent article</a> que les tiers en général (pubs, widgets, trackers, services …) pouvaient vous coûter de l’argent pour des raisons de contre-performance perçue, de distraction, d’atteinte à l’image de marque ou présentaient un risque de fuite de données. Nous avions vu que les solutions techniques existaient mais qu’il faut se laisser le temps de les appliquer lors de l’intégration du tiers (principe “si c’est rapide à intégrer, c’est que ça va ramer”).</p>
<p>Nous venons de voir que la publicité posait ses propres difficultés qui demandent bien plus que les solutions techniques classiques. Les éditeurs ont intérêt à rapidement définir des cahiers des charges et à s’équiper pour s’assurer qu’ils seront respectés pour stopper les pertes de revenus dues aux mauvaises qualités de certaines pubs d’une part, et à l’excès de position d’autre part.
Des éditeurs ont choisi des stratégies d’affichage des publicités qui améliorent grandement l’expérience utilisateur (moins d’emplacements, vendus plus cher) et potentiellement les revenus : qu’attendez-vous pour étudier le sujet ?</p>Cet article et le précédent, plus technique, sur les tiers classiques sont une transcription d’une conférence donnée à Blend Web Mix 2016. Cliquez ici si vous êtes plus audio que texte.Combien coûte un service tiers gratuit ?2016-12-21T15:04:21+00:002016-12-21T15:04:21+00:00/2016/12/21/combien-coute-un-service-tiers-gratuit<p>Vous auriez tort de croire que la réponse est à moitié dans le titre : n’importe quel script ou service externe que vous rajoutez à votre site va vous coûter quelque chose, qu’il soit gratuit ou pas.</p>
<blockquote>
<p>Cet article et <a href="/2016/12/21/les-manques-a-gagner-de-la-publicite-pour-les-editeurs">le suivant sur les pubs</a> sont une transcription d’une conférence donnée à Blend Web Mix 2016. <a href="https://www.youtube.com/watch?v=7tbTm1Jo0gE">Cliquez ici si vous êtes plus audio que texte</a>.</p>
</blockquote>
<h1 id="de-quels-coûts-parle-t-on-">De quels coûts parle-t-on ?</h1>
<p>Ma spécialité étant la performance frontend, j’ai dû m’attaquer au sujet pour des raisons techniques qui m’ont amené à constater que les dégradations en qualité et en performance perçue se traduisaient par un certain nombre de coûts cachés:</p>
<ol>
<li>des <strong>pertes sèches d’activité</strong></li>
<li>un manque à gagner dû au ralentissement du chargement ou au gel de l’interface</li>
<li>une ouverture pour le piratage industriel</li>
<li>une image de marque entamée.</li>
</ol>
<p>Qui sont les tiers ici ?</p>
<ul>
<li>Analytics, tracking</li>
<li>Boutons de partage, réseaux sociaux</li>
<li>Tests A/B</li>
<li>Widgets divers : cartes, commentaires, chats…</li>
</ul>
<p>Et étant donnéées leurs spécificités, citons-les à part : <a href="http://braincracking.org/?p=1256">les <strong>publicités</strong>, auxquelles j’ai dédié un article moins technique</a>.</p>
<p>Passons en revue les problèmes et solutions.</p>
<h1>1. Les 20 secondes page blanche</h1>
<p>Dans le plus spectaculaire des cas, vous pourriez avoir un site qui n’affiche rien pendant plusieurs secondes.</p>
<p>[caption id=”attachment_1199” align=”alignnone” width=”1468”]<a href="http://braincracking.org/wp-content/uploads/2016/12/indisponible.png"><img class="size-full wp-image-1199" alt="20 secondes de page blanche, puis affichage du site" src="http://braincracking.org/wp-content/uploads/2016/12/indisponible.png" width="1468" height="578" /></a> Veuillez patienter, nous allons prendre votre temps.[/caption]</p>
<p>Pour rajouter un peu de sel sur la plaie, disons que c’est le grand patron qui découvre cela un samedi après-midi de vacances. On va alors se lancer dans une partie de Cluedo pour savoir qui a tué le site et les premiers suspects sont les administrateurs système.
Heureusement pour eux, ce sont également fréquemment les seuls à être suffisamment bien équipés en monitoring pour démontrer que les serveurs de la boîte vont bien. Dans le meilleur des cas ils savent mener une analyse rapide du réseau, vue d’un navigateur.</p>
<p>[caption id=”attachment_1200” align=”alignnone” width=”1500”]<a href="http://braincracking.org/wp-content/uploads/2016/12/réseau.png"><img class="size-full wp-image-1200" alt="outil chrome dev tools montrant une requête qui ne répond pas" src="http://braincracking.org/wp-content/uploads/2016/12/réseau.png" width="1500" height="253" /></a> la requête coupable est une longue barre grise, sur-lignée en rouge (forcément)[/caption]</p>
<p>Après analyse, c’est un petit script hébergé sur un serveur qui ne répond pas qui bloque la page. On passe donc en revue les suspect suivants :</p>
<ul>
<li>les développeurs frontend qui n’ont fait que leur devoir en implémentant la solution demandée par le métier,</li>
<li>les services marketing et commercial qui ont légitimement besoin de cette solution de [adserving | test A/B | widget | tracker]</li>
<li>le service tiers, qui avait donné comme garantie au moment de l’intégration : "On est sur un CDN".</li>
</ul>
<h2>L’enfer c’est les autres</h2>
<p>Il est très tentant d’incriminer le tiers à la moindre occasion, et parfois c’est justifié, comme cet incident il y a quelques années où certains tunnels de paiement se mettaient à afficher des erreurs de sécurité.</p>
<p>[caption id=”attachment_1202” align=”alignnone” width=”663”]<a href="http://braincracking.org/wp-content/uploads/2016/12/jquery.png"><img class="size-full wp-image-1202" alt="alerte de sécurité SSL de chrome dûe au serveur jquery" src="http://braincracking.org/wp-content/uploads/2016/12/jquery.png" width="663" height="514" /></a> une alerte de sécurité du plus bel effet au moment de payer[/caption]</p>
<p>Les pages touchées étaient celles en HTTPS qui incluaient jQuery depuis le CDN <code>code.jquery.com</code> … dont l’administrateur avait oublié cette après midi là de renouveler le certificat de sécurité.
Mais il y a plein d’autres raisons pour lesquelles un serveur tiers peut ne pas répondre comme :</p>
<ul>
<li>des attaques mettant à mal votre prestataire ou ses propres prestataires. Dans le cas de <a href="http://dyn.com/blog/dyn-analysis-summary-of-friday-october-21-attack/">l’attaque DDoS contre le DNS Dyn</a>, Twitter n’était plus visible de certains endroits de la planète … tant pis pour les pages incluant les widgets.</li>
<li>des erreurs réseau de l‘hyper-espace comme la redirection temporaire du domaine Google vers un site de prévention du terrorisme du gouvernement … <a href="http://www.bortzmeyer.org/google-detourne-par-orange.html">Uniquement pour les clients Orange un 17 octobre</a> o_O</li>
<li>les wifis gratuits publics (hôtels, aéroports…) qui redirigent certaines requêtes pour insérer dans les pages leurs propres régies publicitaires</li>
<li>les firewalls d'états (Chine en tête) ou les proxys d’entreprise qui ont une opinion tranchée sur l’utilisateur Facebook ou Youtube et qui <a href="https://en.wikipedia.org/wiki/Websites_blocked_in_mainland_China">bloquent les domaines</a> : dommage pour les widgets et analytics</li>
</ul>
<p>Bref : il y a 1001 raisons pour qu’un serveur tiers plante ou ralentisse, il faut donc prévoir son intégration avec cet état de fait.</p>
<h2>Savoir intégrer les tiers</h2>
<p>Généralement l’intégrateur reçoit simplement comme instruction un mail disant :</p>
<blockquote>Insérez ce script dans le <code><head></code></blockquote>
<p>J’exagère un peu, parfois on a droit à un document Word formaté proprement (mais qui dit la même chose).
Il arrive aussi que les développeurs décident d’eux même de faire une bêtise :</p>
<ul>
<li>inclusion de librairies JS / CSS directement depuis leurs CDNs respectifs, comme le préconisent les documentations,</li>
<li>récupération des fichiers de polices depuis les serveurs Google Fonts ou autre.</li>
</ul>
<p>Dans ce dernier cas, il peut y avoir l’excuse de la licence qui n’autorise pas forcément le rapatriement du fichier. Du coup vous faites subir à vos utilisateurs le comportement par défaut de tous les navigateurs (sauf <abbr title="Internet Explorer et Edge">IE</abbr>) qui n’affichent pas le texte tant que le fichier de police n’est pas présent.</p>
<h3>Je vois pas le problème</h3>
<p>Pour visualiser les problèmes d’intégration, et surtout leurs conséquences pour l’utilisateur, je ne saurais trop vous recommander les outils suivants :</p>
<ul>
<li>L’indispensable service en ligne <a href="http://www.webpagetest.org/">WebPagetest</a> : déplier "Advanced Settings", onglet "<abbr title="Single Point Of Failure">SPOF</abbr>" et y mettre les noms d’hôtes à faire planter.</li>
<li>L’extension Chrome <a href="https://chrome.google.com/webstore/detail/spof-o-matic/plikhggfbplemddobondkeogomgoodeg">SPOF-o-Matic</a> qui permet d’alerter, de tester et même de programmer un test comparatif sur WebPagetest.</li>
<li>En ligne de commande (pour l’intégration continue par exemple), je vous ai fait un petit script par dessus <a href="https://gist.github.com/jpvincent/494117fc2806a5d14806cc96a6354cef">SPOF-check</a></li>
</ul>
<p>[caption id=”attachment_1207” align=”alignnone” width=”663”]<a href="http://braincracking.org/wp-content/uploads/2016/12/spof-o-matic.png"><img class="size-full wp-image-1207" alt="capture de l’interface SPOF-o-Matic" src="http://braincracking.org/wp-content/uploads/2016/12/spof-o-matic.png" width="663" height="365" /></a> l’extension SPOF-o-Matic débusquant les potentiels fauteurs de troubles[/caption]</p>
<h3>Solutions</h3>
<p>Pour ne jamais dépendre d’un tiers, il n’y a qu’environ 2 stratégies :</p>
<ol>
<li>Les ressources <strong>critiques</strong> sont à rapatrier sur vos propres serveurs. Le CSS bien sûr, les dépendances <abbr title="JavaScript">JS</abbr>, les polices si vous en avez le droit.</li>
<li>Tout ce qui reste hébergé ailleurs doit passer en asynchrone : trackers, analytics, widgets, <a href="https://www.zachleat.com/web/comprehensive-webfonts/">les polices</a>…</li>
</ol>
<p>Il y a certains cas spéciaux comme l’inclusion des tests A/B qui sont à la fois dynamiques et critiques pour le rendu de la page, étant donné qu’ils essayent de le modifier. Mais si vous demandez poliment une mécanique de “timeout” ou un rapatriement périodique du fichier, vous n’aurez plus de soucis.</p>
<h1>2. Les retards à l’allumage</h1>
<p>Une fois traité le problème impressionnant mais rare de la page blanche dûe à un tiers, reste le problème sournois du ralentissement quotidien, moins visible mais bien plus coûteux. Même avec des inclusions asynchrones on peut se retrouver dans ces situations</p>
<p>[caption id=”attachment_1208” align=”alignnone” width=”916”]<a href="http://braincracking.org/wp-content/uploads/2016/12/ralentissement-fonctionnalite.png"><img class="size-full wp-image-1208" alt="un moteur de recherche de voyages mis en attente" src="http://braincracking.org/wp-content/uploads/2016/12/ralentissement-fonctionnalite.png" width="916" height="742" /></a> Moteur de recherche (95% du business) attendant une régie pub (5% du business)[/caption]</p>
<p>Beaucoup (trop) de fonctionnalités dépendent complètement de JavaScript et de moins en moins de développeurs tentent <a href="http://www.pompage.net/traduction/degradation-elegante-et-amelioration-progressive">l’amélioration progressive</a>. Dans certains cas comme ici un moteur de recherche complexe, il est même irréaliste de vouloir afficher un moteur sans JS, d’où l’idée de mettre une interface de chargement pour patienter. En sachant que ce code métier critique est inclus en haut de page, quand l’exécuter ? Du pire au mieux :</p>
<ol>
<li>en haut de page ! Bien sur que <strong>NON</strong>, le <abbr title="arbre HTML interprété">DOM</abbr> n’est pas forcément disponible à ce moment-là (ce qui ne se voit pas forcément quand on développe sur son poste en local d’ailleurs :) )</li>
<li>on fait confiance à StackOverflow et on attend que <code>document.readyState === 'complete'</code> … Cela correspond à l’événement <code>window.load</code>, c’est-à-dire qu’on va attendre patiemment que toutes les images, <code>iframes</code> et même certains scripts asynchrones soient chargés ! L’utilisateur n’attendra pas, lui.</li>
<li>on attend l’événement <code>document.DOMContentLoaded</code> (en jQuery : <code>$(document).ready(init)</code> ou juste <code>$(init)</code>). Cet événement est retardé par les insertions de scripts même en bas de page, les scripts asynchrones avec l’attribut <code>defer</code> et toute exécution de script, dont le tueur de performances <code>document.write()</code>. La pub utilise toujours cette instruction.</li>
<li>juste après le code HTML du formulaire : vous avez à la fois la garantie que le DOM est présent, et d’être exécuté avant tous les événements navigateur. C’est ce que je recommande pour du code critique comme ce formulaire.</li>
</ol>
<p>Il y a donc un conflit ouvert entre le tiers inclus et votre code métier, que vous pouvez résoudre en exécutant plus tôt votre code. Si vous persistez à vouloir dépendre de l’événement <code>DOMContentLoaded</code> pour exécuter votre code, il va falloir travailler au corps l’inclusion des scripts des tiers :</p>
<ul>
<li>s’ils ne sont pas lourds à l’exécution et importants, le plus bas possible dans la source avec l’attribut standard <code>async</code> (IE10 +)</li>
<li>s’ils sont secondaires, en inclusion asynchrone classique après le chargement de la page (natif <a href="https://gist.github.com/jpvincent/51321638dee74e2dd8f53bada8a47ee5">façon Analytics</a> ou <a href="https://api.jquery.com/jquery.getscript/">$.getScript</a>)</li>
<li>refuser les scripts dépendant ou utilisant <code>document.write()</code> (d’ailleurs <a href="https://developers.google.com/web/updates/2016/08/removing-document-write">partiellement dépréciée</a> par Chrome).</li>
</ul>
<p>La pub est spéciale : les régies ne peuvent pas se passer de l’instruction <code>document.write()</code> car leurs propres fournisseurs l’utilisent <strong>peut-être</strong>. La solution technique est simplement d’inclure la publicité en <code>iframe</code>, ce qui interdit certains formats invasifs. Désolé de vous le dire mais si votre business dépend de la pub et que vous vous êtes engagé à afficher des pubs qui peuvent déborder de leur cadre, il n’y a pas de solution purement technique ! On va voir <a href="http://braincracking.org/2016/12/21/les-manques-a-gagner-de-la-publicite-pour-les-editeurs/">dans un autre article</a> qu’il y a matière à bien faire, mais que les décisions sont loins d’être techniques.</p>
<h2>Après le chargement</h2>
<p>Vous avez forcément déjà constaté les effets d’interface gelée voire de crash sur mobile, ou vous avez eu l’impression que votre ordinateur portable allait décoller de votre bureau par la seule force de ses ventilateurs. Pour faire le test, je vous conseille de visiter cette page parodique <strong>sans adblock</strong>, voire sur mobile : <a href="http://worldsmostshareablewebsite.greig.cc/">http://worldsmostshareablewebsite.greig.cc/</a>, qui se contente d’inclure plusieurs fournisseurs de boutons de partage. Sur une tablette Nexus 7, l’interface est gelée pendant 20 secondes.
La nouvelle bataille de la performance frontend, est clairement les temps d’exécution abusifs côté client. J’ai déjà eu le cas avec de “simples” inclusions de bouton de partage (Facebook et G+ en tête) mais c’est le cas avec n’importe quel script qui va modifier l’interface. Il y a un principe général à comprendre avec les tiers :</p>
<blockquote>Si c’est rapide à intégrer, c’est que ça va ramer</blockquote>
<p>Les tiers, en particulier s’ils ont une version gratuite, ont 2 besoins antinomiques : simple et passe-partout. Le tutoriel doit paraître simple et consiste généralement en une inclusion de script et à la création d’un peu de HTML. À l’image du bouton Facebook.</p>
<p>[caption id=”attachment_1212” align=”alignnone” width=”540”]<a href="http://braincracking.org/wp-content/uploads/2016/12/facebook.png"><img class="size-full wp-image-1212" alt="code d’inclusion du bouton facebook" src="http://braincracking.org/wp-content/uploads/2016/12/facebook.png" width="540" height="356" /></a> Trop simple pour être performant[/caption]</p>
<p>Remarquez que l’insertion du script est généralement asynchrone comme ici, car les tiers ont fait des efforts pour ne pas bloquer les chargements ces dernières années.</p>
<h3>Détecter le problème</h3>
<p>Maintenant mettez vous à la place du développeur de ce code : comment faire pour savoir où afficher son widget ? La réponse est aussi simple que contre-performante : en s’auto-exécutant dès que possible, et en scannant le DOM régulièrement. Selon la lourdeur initiale de votre site, on peut vite arriver à de jolis blocages dont vous ne comprendrez l’origine qu’avec un profilage en règle. Notez qu’il faut utiliser de vraies machines pour tester correctement tout cela.</p>
<p>[caption id=”attachment_1214” align=”alignnone” width=”1119”]<a href="http://braincracking.org/wp-content/uploads/2016/12/profiler.png"><img class="size-full wp-image-1214" alt="capture d’écran de l’outil de profilage chrome" src="http://braincracking.org/wp-content/uploads/2016/12/profiler.png" width="1119" height="566" /></a> 3 longues secondes d’exécution pour ce seul tiers[/caption]</p>
<p>Le fichier mis en cause ici (<code>post-widget.js</code> surligné en gris) provient effectivement d’un tiers qui inclut des boutons sociaux. Il bloquait l’interface et faisait même régulièrement planter un iPad mini 2. À sa décharge, la page visée était lourde et contenait 150 éléments que le script recherchait et modifiait.
Il n’existait pas d’outil dédié — même payant — permettant de déceler facilement qui était responsable des pics de charge processeur sur une page. J’ai du écrire un petit script du nom de <a href="https://github.com/jpvincent/3rd-party-cpu-abuser">3rd-party-cpu-abuser</a> qui exploite la timeline de Chrome Dev Tools pour mettre des noms sur les responsables :</p>
<p>[caption id=”attachment_1215” align=”alignnone” width=”417”]<a href="http://braincracking.org/wp-content/uploads/2016/12/3rd-party-abuser.png"><img class="size-full wp-image-1215" alt="tableau de statistiques de consommation CPU" src="http://braincracking.org/wp-content/uploads/2016/12/3rd-party-abuser.png" width="417" height="667" /></a> 80% du <abbr title="processeur">CPU</abbr> consommé l’est par les tiers[/caption]</p>
<p>WebPagetest a également récemment introduit une visualisation du CPU répartie par fichier JS : onglet “chrome”, case “capture devtools timeline”).</p>
<p>Pour débloquer la situation, je me suis contenté comme souvent d’aller lire la documentation pour y chercher 2 optimisations classiques :</p>
<ul>
<li>comment <strong>ne pas</strong> initialiser le script automatiquement, afin d’éviter l’effet Big Bang du début de construction de page et ne l’exécuter qu’au moment opportun.</li>
<li>comment indiquer le DOM à modifier, pour éviter les recherches DOM coûteuses</li>
</ul>
<p>Pratiquement tous les fournisseurs de widget proposent maintenant ces options dans leurs APIs. Si ce n’est pas le cas, c’est mauvais signe donc partez chez le concurrent !</p>
<h1>3. L’espionnage industriel</h1>
<h2>Les réseaux sociaux</h2>
<h3>La performance</h3>
<p>Rajouter quelques boutons de partage n’est pas aussi gratuit que ça en a l’air. Il y a bien sûr la performance utilisateur, qui en pâtit.</p>
<p>[caption id=”attachment_1218” align=”alignnone” width=”1500”]<a href="http://braincracking.org/wp-content/uploads/2016/12/buttons.png"><img class="size-full wp-image-1218" alt="comparaison de screenshot montrant le chargement d’une page wordpress avec et sans boutons" src="http://braincracking.org/wp-content/uploads/2016/12/buttons.png" width="1500" height="330" /></a> 4 boutons = ralentissement de l’affichage et du temps de chargement[/caption]</p>
<p>Sur cette <a href="http://wpt.fasterize.com/video/compare.php?tests=161027_KT_15,161027_3X_14">page wordpress simple</a> et optimisée, rajouter 4 boutons natifs multiplie le temps de 1er affichage et de chargement par deux !</p>
<h3>Les taux de transformation</h3>
<p>Mais il faut aussi se demander si ces boutons apportent vraiment quelque chose au site. Par exemple Smashing Mag en 2012 avait constaté qu’en ENLEVANT le bouton Facebook, leurs utilisateurs partageaient plus les articles sur Facebook !</p>
<p>[caption id=”attachment_1219” align=”alignnone” width=”839”]<a href="http://braincracking.org/wp-content/uploads/2016/12/smashing.png"><img class="size-full wp-image-1219" alt="le traffic depuis Facebook a augmenté depuis que nous avons enlevé les boutons FaceBook de nos articles" src="http://braincracking.org/wp-content/uploads/2016/12/smashing.png" width="839" height="465" /></a> Smashing Mag : “le trafic depuis Facebook a augmenté depuis que nous avons enlevé les boutons FaceBook de nos articles” (<a href="https://twitter.com/smashingmag/status/204955763368660992">2012</a>)[/caption]</p>
<p>NB: Entretemps Facebook a sorti un bouton “Partage” plutôt que simplement “Like”, et Smashing Mag l’a ré-intégré de manière statique.</p>
<p>Autre exemple : un <a href="https://vwo.com/blog/removing-social-sharing-buttons-from-ecommerce-product-page-increase-conversions/">test A/B fait par un site de e-commerce</a> qui a consisté à <strong>SUPPRIMER ces boutons</strong> de la page produit et leur a permis de constater que cette suppression <strong>AUGMENTAIT les taux de conversion de 12%</strong>. Ils supposent que finalement ces boutons n’étaient qu’une distraction à ce moment-là, et que les chiffres de partage affichés, proches de 0 comme souvent dans le e-commerce, renvoyaient un sentiment négatif. J’y ajouterais que la légère dégradation de performance induite par ces boutons a dû contribuer :)</p>
<p>Bref : comme pour n’importe quelle fonctionnalité, demandez vous si elle ne dessert pas plutôt votre produit, ou s’il n’y a pas une meilleure manière de l’intégrer.</p>
<h3>Nourrir vos concurrents</h3>
<p>La raison d’être secondaire de ces boutons pour Google et Facebook est qu’il leur permet de mieux qualifier leurs propres utilisateurs, en dehors de leurs propres murs. Même lorsque l’utilisateur n’interagit pas avec, leur simple présence signale à G. et FB qu’un de leurs utilisateurs visite une page, leur permettant ainsi d’affiner les profils. Dans le cas de Google, l’utilisateur n’a même pas besoin d’avoir un compte. Ne me lancez pas sur le respect de la vie privée, <a href="https://www.miximum.fr/blog/conf-pw-2017/">d’autres le font mieux que moi</a>, parlons plutôt argent.
À toute fin utile, il faut tout de même rappeler que Google mange 42% du marché de la publicité en France (95% des 47% du <abbr title="Chiffre d’affaire">CA</abbr> de la search).</p>
<p>[caption id=”attachment_1220” align=”alignnone” width=”466”]<a href="http://braincracking.org/wp-content/uploads/2016/12/marché-pub.png"><img class="size-full wp-image-1220" alt="répartition du marché de la publicité en 2014 : 47% pour le search et 31% pour le display" src="http://braincracking.org/wp-content/uploads/2016/12/marché-pub.png" width="466" height="472" /></a> <a href="http://www.iabturkiye.org/sites/default/files/iab_europe_adex_benchmark_2014_report.pdf">Marché de la pub en 2014</a>[/caption]</p>
<p>Pour le marché qui vous intéresse probablement, c’est-à-dire le “display” (affichage de bannières classiques) <a href="https://piacentino.com/jb/2015/publicite-en-ligne-ou-va-largent">on estime</a> que Facebook en capte 30%. Ce que les annonceurs apprécient, outre l’audience, c’est que leur base est hyper qualifiée, et c’est en partie grâce aux sites qui incluent les boutons.
Déjà que ces boîtes s’évitent un maximum d’impôts en France, ce n’est pas la peine de leur faire de cadeau supplémentaire en utilisant leurs scripts tels quels.</p>
<h3>Les solutions</h3>
<p>En admettant que vous ayez déterminé que les boutons de partage étaient vraiment bons pour votre site (typiquement, un site d’articles), il existe des manières d’inclure qui ne nourrissent pas vos concurrents, sont performantes et respectent votre charte graphique :</p>
<ol>
<li>les boutons statiques, qui sont <a href="https://github.com/bradvin/social-share-urls#google">des liens simples formatés permettant le partage</a> et que vous pouvez mettre par dessus de simples icônes</li>
<li>si vous avez besoin de fonctionnalités supplémentaires comme des icônes toutes faites, afficher les compteurs ou avoir des analytics, vous pouvez rajouter un peu de JavaScript. Voici une <a href="https://github.com/finderau/share-buttons">solution open-source</a> parmi d’autres</li>
<li>enfin il existe quantité de services tiers (addThis, shareThis, shareAholic …) mais on retombe sur les travers habituels des tiers : il faut <a href="https://www.xfive.co/blog/social-media-buttons-test-performance-privacy-features/">benchmarker les temps de chargement</a> car ils vont du simple au triple et le business model de certains comme shareAholic est de revendre les données utilisateur</li>
</ol>
<h2>Les tiers des tiers de vos tiers</h2>
<h3>Le problème</h3>
<blockquote>Quand il n’y en a qu’un ça va</blockquote>
<p>S’il est normal de faire confiance à un prestataire direct, la confiance ne peut que s’étioler au fur et à mesure de la chaîne d’appel de leurs partenaires. Or quand vous accordez techniquement les pleins pouvoirs à votre prestataire (une simple insertion de script), vous les accordez également à toute une chaîne de sociétés dont vous n’aviez peut-être jamais entendu parler. Je vais utiliser l’outil <a href="http://requestmap.webperf.tools/">RequestMap</a> pour visualiser un affichage sur la Une du journal Le Monde.</p>
<p>[caption id=”attachment_1223” align=”alignnone” width=”778”]<a href="http://braincracking.org/wp-content/uploads/2016/12/lemonde-niveau1.png"><img class="size-full wp-image-1223" alt="visualisation des requêtes faites vers les 1er tiers" src="http://braincracking.org/wp-content/uploads/2016/12/lemonde-niveau1.png" width="778" height="1108" /></a> Appels vers les tiers de 1er niveau[/caption]</p>
<p>Chaque cercle est un nom de domaine, les flèches sont les appels. Au centre en bleu clair, le domaine principal <code>www.lemonde.fr</code> qui appelle 2 sous-domaines de statiques (<code>s1</code> et <code>s2.lemde.fr</code>). Tous les autres domaines (cercles) appartiennent aux tiers de 1er niveau et correspondent à une <strong>quinzaine de sociétés</strong>. On y trouve 3 solutions d’analytics, une solution de test A/B (Kameleoon), un tracking de réseau social (Po.st), FaceBook, un moteur de recommandation d’articles (Outbrain ici), Cedexis … Ce sont les tiers de 1er niveau : ceux que l’on inclut de manière tout à fait consciente. Les relations avec ces tiers sont régies par un contrat direct ou par l’acceptation des <abbr title="Conditions Générales d’Utilisation">CGU</abbr>, et tant que les principes d’intégration cités plus haut dans cet article sont respectés, je n’ai plus rien à rajouter.</p>
<p>Puis arrive le moment où l’on contacte la régie pub (SmartAd en l’occurrence, à droite sur le graphe précédent, à gauche sur le graphe suivant).</p>
<p>[caption id=”attachment_1225” align=”alignnone” width=”1006”]<a href="http://braincracking.org/wp-content/uploads/2016/12/lemonde-2.png"><img class="size-full wp-image-1225" alt="Tiers de niveau 2, 3 et 4 … Oh la belle rose" src="http://braincracking.org/wp-content/uploads/2016/12/lemonde-2.png" width="1006" height="1109" /></a>Appel des tiers de niveau 2, 3 et 4 … Oh la belle rose[/caption]
Une fois sur son domaine, la régie appelle une <strong>dizaine de sociétés</strong>, principalement des tags de tracking, puis 2 systèmes de mise aux enchères de mon profil utilisateur. C’est la société de <abbr title="Real Time Bidding">RTB</abbr> CasaleMedia qui prend le relai, et qui contacte une <strong>vingtaine d’autres sociétés</strong>. Cela c’est côté client, on ne sait pas combien d’enchérisseur ont accès au profil utilisateur côté serveur. L’enchère a été gagnée par une société de retargeting (Turn) qui elle-même contacte <strong>une vingtaine de sociétés</strong>.
En tout c’est 50 sociétés qui ont été contactées côté client. Si le script du 1er tiers inclus de manière classique, <strong>ces 50 sociétés avec qui vous n’avez aucun contrat ont tout pouvoir sur la page</strong>. Il leur est techniquement possible de la modifier (on a vu des sociétés malhonnêtes remplacer les publicités des autres) ou d’y récupérer des informations : outre les informations de navigation, elles pourraient cibler un site de e-commerce et estimer ses ventes et leur nature par exemple, simplement en scannant le DOM. Ces scénarii sont pratiquement indétectables et quand on voit qu’il arrive que des pirates <a href="https://blog.malwarebytes.com/threat-analysis/2016/01/msn-home-page-drops-more-malware-via-malvertising/">diffusent des virus grâce aux régies publicitaires</a>, il n’est pas irréaliste d’imaginer une société voulant récupérer des informations chez son concurrent.</p>
<h3>Les solutions</h3>
<h4>Se parler !</h4>
<p>Les premières actions ne sont pas techniques du tout.
D’abord, lister quelles sociétés atterrissent sur vos pages, avec des solutions :</p>
<ul>
<li>open-source, comme <a href="https://www.mozilla.org/fr/lightbeam/">LightBeam</a> et <a href="https://www.eff.org/fr/privacybadger">privacy-badger</a></li>
<li>commerciales, comme le populaire <a href="https://www.ghostery.com/">Ghostery</a></li>
</ul>
<p>Ensuite, du côté des contrats signés avec vos partenaires, vérifiez que leurs partenaires à eux vous conviennent et demandez-vous quels engagements de moyens ils ont et pourquoi pas imaginer un système de pénalités. Les régies savent se montrer réactives lorsqu’un de leurs clients leur signale un problème avec un prestataire, mais détecter le problème en question est très compliqué.
Enfin, il faut établir un dialogue entre les équipes IT et les utilisateurs des services tiers (marketing, produit, commerciaux …), en faisant un point régulier “Tiers”, avec l’ordre du jour suivant :</p>
<ol>
<li>l’IT fait l’inventaire des tiers de 1er niveau et remontent éventuellement les problèmes rencontrés</li>
<li>côté business, on classe par ordre d’importance les tiers … oui il va y avoir du débat !</li>
<li>décisions de suppression</li>
<li>décisions quant à l’ordre de chargement</li>
</ol>
<p>On se retrouve à prendre des décisions complexes car comment décider de ce qui est plus important entre un test A/B, les analytics, le player vidéo, les pubs, la carte ou les boutons de partage ? Il faut pourtant que ce soit une décision réfléchie car jusqu’ici, cette décision était de fait laissée à l’intégrateur et au navigateur …</p>
<h4>Côté technique</h4>
<p>Si votre business model vous permet de vous passer des publicités invasives, ce qui est le cas des sites de e-commerce par exemple, alors je ne saurais trop vous recommander la bonne vieille technique de l’inclusion en <code>iframe</code>. Cela gomme les inconvénients de <code>document.write()</code>. L’attribut <code>sandbox</code> vous permet (depuis IE9) d’accorder aux tiers d’accéder à votre page, vos cookies ou le droit d’ouvrir une popup.
<code><iframe sandbox="allow-scripts" src="carre_pub.html" /></code> suffit pour les publicités classiques : il autorise JavaScript, mais la sous-page ne peut rien faire sur la page principale. Si vous avez besoin de communiquer, l’API <a href="https://developer.mozilla.org/fr/docs/Web/API/Window/postMessage">postMessage</a> est là pour ça !</p>
<p>Pour aller plus loin, mais je ne l’ai encore jamais vu en production, il y a le standard <a href="https://content-security-policy.com/">Content Security Policy</a> (à partir de Edge) qui permet entre autres de limiter les pouvoirs donnés aux tiers inclus directement en JavaScript. Pour une transition en douceur, vous pouvez passer par une phase d’observation des erreurs côté client (<code>report-uri</code>). Maintenant on ne va pas se mentir : la vraie difficulté, c’est de faire admettre que l’on veut limiter le pouvoir des tiers, pas de les en empêcher !</p>
<h1>Conclusion</h1>
<p>Les tiers posent leurs lots de problèmes mais ils ont pratiquement tous une solution technique, <a href="http://braincracking.org/?p=1256">au contraire des publicités</a>. Du moment que l’on fait attention au moment de l’intégration et que l’on surveille les régressions de performance, on évite la plupart des écueils. Si vous avez plus de quelques services externes et qu’ils peuvent mettre en péril votre business, mettre en place une solution de monitoring dédiée est à envisager rapidement.</p>Vous auriez tort de croire que la réponse est à moitié dans le titre : n’importe quel script ou service externe que vous rajoutez à votre site va vous coûter quelque chose, qu’il soit gratuit ou pas.Le web mobile et la performance2014-12-12T15:04:21+00:002014-12-12T15:04:21+00:00/2014/12/12/le-web-mobile-et-la-performance<p>Cet article a été rédigé initialement pour 24 jours de web : http://www.24joursdeweb.fr/2014/le-web-mobile-et-la-performance/</p>
<p>Tout site sortant aujourd’hui doit avoir été pensé avec des contraintes mobiles, et utiliser des techniques d’amélioration progressive pour délivrer rapidement les fonctionnalités sur toutes plateformes.</p>
<p>En attendant cette conclusion, nous commencerons par essayer de connaître notre utilisateur, puis nous verrons les tactiques et stratégies propres au mobile.</p>
<h2>Où est le problème ?</h2>
<p>La <abbr title="Téléphonie 4e génération">4G</abbr> a débarqué, des mobiles à 8 processeurs se vendent, et la plupart des gens utilisent leur mobile depuis chez eux : en Wifi ! À priori le métier de spécialiste performance web serait donc mort et vous me trouverez en train de parler <abbr title="Hypertext markup language, langage de marquage hypertexte">HTML</abbr>5 à mes chèvres dans le Larzac, merci au revoir. À moins que…</p>
<h3>L’utilisation</h3>
<p>La « situation de mobilité » est-elle un mythe ? L’expérience et une <a href="http://services.google.com/fh/files/misc/omp-2013-fr-local.pdf">étude Google</a>montrent clairement que les ¾ des gens utilisent majoritairement leur mobile depuis le confort de leur canapé / lit / lunette, derrière le wifi familial. 15% de ces mêmes Français ont même la fibre.</p>
<p>Le temps passé dans une situation confortable est majoritaire mais cela est justement dû à <strong>la réalité de la situation de mobilité : cela doit être très rapide</strong>. Dans un magasin, les gens vérifient les prix des concurrents sur le Web : si votre page ne s’affiche pas en plus de quelques secondes, vous avez perdu la guerre des prix dès la première bataille (afficher le prix) ! Dans la rue, l’utilisateur va vouloir connaître les horaires de cinéma ou les restaurants du coin : si c’est votre coeur de métier vous n’aurez toutes vos chances qu’en affichant rapidement l’information demandée. Dans les situations d’attente, les gens dégainent les téléphones pour consulter réseaux sociaux et mails : si votre page a la chance d’être dans leur fil d’information, il va falloir afficher la promesse très rapidement (un article sur les félins à travers les âges, une vidéo de chats, le lolcat du jour…). Enfin la préparation d’achat se fait fréquemment sur mobile (plus discret dans l’open space, à portée de main lors des pauses clope voire au feu rouge), même si la finalisation de la commande se fait encore majoritairement sur l’ordinateur du foyer voire sur tablette.</p>
<p>Tout cela ne compte que pour quelques secondes dans les statistiques mais les utilisateurs se souviendront de votre marque de manière très positive si vous avez résolu leur problème rapidement.</p>
<h3>La connexion</h3>
<p>Les données de cette année montrent clairement une amélioration des débits <abbr title="Téléphonie de 3e génération">3G</abbr> (6 <abbr title="Megabytes">Mb</abbr>/<abbr title="seconde">s</abbr> en moyenne selon Akamai) et la <abbr>4G</abbr> à la française envoie du poney au galop (21-32 <abbr>Mb</abbr>/<abbr>s</abbr> <strong>constatés</strong> selon <a href="http://www.degrouptest.com/publications/14/Barometre-Internet-mobiles/4">Degrouptest</a>, pointes de 34 <abbr>Mb</abbr>/<abbr>s</abbr> selon <a href="http://www.akamai.com/dl/whitepapers/akamai-soti-a4-q214.pdf">Akamai</a>).</p>
<p>Mais si vous êtes utilisateur régulier du mobile, vous devinez la vérité derrière ces excellentes moyennes : même avec une puce et un abonnement 4G vous ne chargez parfois pas du tout les pages et vous esquissez un sourire triomphant lorsqu’un site web se charge aussi bien qu’au bureau. C’est parce que l’ennemi numéro 1 du chargement de page web reste la <strong>latence réseau</strong>, et même sur une <abbr>4G</abbr> de bonne qualité les latences constatés par Degrouptest sont entre 50 et 100 <abbr title="millisecondes">ms</abbr>.</p>
<p>Concrètement :</p>
<ul>
<li>ligne du haut : une <abbr>4G</abbr> de cadre parisien avec <strong>20 <abbr>Mb</abbr>/<abbr>s</abbr> de débit</strong> mais 200 <abbr>ms</abbr> de latence (les murs de Paris sont épais),</li>
<li>ligne du bas : l’<abbr title="haut débit asymétrique">ADSL</abbr> de la mamie du Cantal avec <strong>2 <abbr>Mb</abbr>/<abbr>s</abbr></strong> et 20 <abbr>ms</abbr> de latence (oui son <abbr title="nœud de raccordement local au réseau">DSLAM</abbr> est proche).</li>
</ul>
<p><a href="http://media.24joursdeweb.fr/2014/12/debit-vs-latence.jpg"><img alt="La mamie du Cantal gagne (et avec un ping pareil, elle frag aussi de l’ado à tout va). " src="http://media.24joursdeweb.fr/2014/12/debit-vs-latence.jpg" width="715" height="290" /></a></p>
<p>La mamie du Cantal gagne
(et avec un ping pareil, elle frag aussi de l’ado à tout va).</p>
<p>L’affichage via la connexion à 2 <abbr>Mb</abbr>/<abbr>s</abbr> commence 1 seconde plus tôt, <strong>le formulaire est utilisable 2 secondes plus tôt</strong> et le site au complet se charge 5 secondes plus vite.</p>
<p>Ajoutez à cela que les réseaux <abbr>3G</abbr> des grandes villes sont saturées, et qu’en fonction de l’endroit, de l’heure de la journée, du jour de la semaine et de la position de votre doigt sur le mobile, les performances ne sont pas du tout les mêmes. Dans certains cas pas un octet ne passe.</p>
<p>Tout ça pour dire que les grands principes de la <strong>limitation du nombre de requêtes</strong> d’abord, puis <strong>du poids du contenu</strong> restent valables.</p>
<h3>L’utilisateur</h3>
<p>Tout le monde n’investit pas dans un bidule avec 8 processeurs et 3 <abbr title="Giga-octets">Go</abbr> de <abbr title="mémoire vive">RAM</abbr>, par contre les utilisateurs mobiles sont clairement habitués à ce que les interfaces soient fluides et réactives, et même à ce qu’on leur affiche des données en l’absence de réseau. Ajoutez à cela le fait que l’on sert fréquemment des sites mobiles minimalistes et vous comprendrez que l’utilisateur a bien du mal à accepter que les sites qu’il consulte mettent du temps à apparaître, ou laggent lorsqu’il scrolle.</p>
<p>Du coup un tombereau d’études utilisateur ou d’analyse statistiques démontrent ce que l’on pressent déjà : le temps coûte cher.</p>
<p>Absolument tous les indicateurs marketing sont touchés :</p>
<ul>
<li>le taux de rebond (<a href="http://radar.oreilly.com/2014/01/web-performance-is-user-experience.html" hreflang="en">Etsy</a> : ajouter 160<abbr title="Kilo-octets">Ko</abbr> d’images = +12% de taux de rebond),</li>
<li>le taux de clic (<a href="http://doubleclickadvertisers.blogspot.fr/2011/06/cranking-up-speed-of-dfa-leads-to.html" hreflang="en">DoubleClick</a> : supprimer 1 redirection = +12% de taux de clic),</li>
<li>l’abandon pur et simple de la page (<a href="http://www.radware.com/PleaseRegister.aspx?returnUrl=6442454446&ref=prodoverview" hreflang="en">Radware</a> : 60% d’abandon après 4 secondes de page blanche),</li>
<li>l’abandon du processus d’achat (<a href="http://minus.com/msM8y8nyh#1e" hreflang="en">Wallmart</a> : -50% de conversion par seconde),</li>
<li>l’abandon du visionnage (<a href="http://people.cs.umass.edu/~ramesh/Site/HOME_files/imc208-krishnan.pdf" hreflang="en">Akamai</a> : -6% de vidéo vue par seconde d’attente),</li>
<li>la <a href="http://calendar.perfplanet.com/2013/slow-pages-damage-perception/" hreflang="en">perception négative</a> de la marque.</li>
</ul>
<p>Le côté amusant de l’histoire : si vous surveillez vos taux de rebond sur mobile en vous disant « ça va encore, » dites-vous que les statistiques sont en dessous de la réalité puisque dans les cas des pages blanches, il est probable que la requête remontant sur le serveur qui comptabilise les hits ne soit jamais partie.</p>
<h3>Nos sites !</h3>
<p>Étant donné que les grands chefs regardent les sites sur des mobiles qui valent <a href="http://store.apple.com/fr/buy-iphone/iphone6/%C3%A9cran-5,5%C2%A0pouces-128go-or-d%C3%A9verrouill%C3%A9">le prix de ma première voiture</a>, via le wifi de la boite et qu’ils s’arrêtent à la homepage, forcément il y a un peu de laisser-aller sur les sites, y compris dédiés mobile. Le poids des sites dits dédiés mobile (mDot) <a href="http://www.webperformancetoday.com/2014/05/28/mobile-optimized-pages-bigger/" hreflang="en">augmente régulièrement</a>. La bonne idée d’avoir un site mobile dépouillé et ergonomiquement efficace s’efface pour laisser la place aux mauvaises habitudes des sites desktop : on met tout sur la HomePage, et on rajoute un maximum d’information sur les pages intérieures.</p>
<p>Le <abbr title="responsive web design, design web réactif">RWD</abbr> devient la norme pour les refontes graphiques, mais rarement le « <i lang="en">mobile first</i> » : on se retrouve donc avec des spécifications planifiant 5 <i lang="en">breakpoints</i>, devant afficher des images « retina » tout en gérant <abbr title="Internet Explorer">IE</abbr> 6 (oui cette spécification existe vraiment). Le poids des sites en général a doublé en 3 ans et grâce au <abbr>RWD</abbr> on demande maintenant d’afficher des sites de 2 <abbr>Mo</abbr> et 100 requêtes sur mobile. Ça finirait par arriver si l’utilisateur était encore là pour le voir.</p>
<p>Sur ces même sites <i lang="en">cross-device</i>, votre département marketing a bien sûr opté pour la totale :</p>
<ul>
<li>de l’<i lang="en">analytics</i> (augmentation du nombre de requêtes),</li>
<li>des publicités (poids, temps d’exécution, requêtes),</li>
<li>de l’<i lang="en">A/B Testing</i> (retardement <strong>volontaire</strong> de l’affichage, temps d’exécution),</li>
<li>des boutons sociaux (le moindre bouton G+ rajoute 100 <abbr>Ko</abbr> et des dizaines de requêtes)</li>
</ul>
<p>Et les tendances webdesign du moment ne sont plus aux coins arrondis qu’on aurait pu régler en CSS3 en pouffant, mais aux polices de caractère (blocage temporaire du rendu, texte invisible, poids), aux « <i lang="en">hero images</i> » (l’image principale en 1200x800, <i lang="en">retina-ready</i>, cordialement) ou pire aux <i lang="en">slideshows</i> (<a href="http://www.doisjeutiliser.fr/unCarrousel/">pourtant une mauvaise idée</a>) qui peuvent charger de grosses images invisibles.</p>
<p><a href="http://media.24joursdeweb.fr/2014/12/chargement-pmvc.jpg"><img alt="Chargement du site Promovacances" src="http://media.24joursdeweb.fr/2014/12/chargement-pmvc-860x82.jpg" srcset="http://media.24joursdeweb.fr/2014/12/chargement-pmvc-860x82.jpg 860w, http://media.24joursdeweb.fr/2014/12/chargement-pmvc-430x41.jpg 430w, http://media.24joursdeweb.fr/2014/12/chargement-pmvc.jpg 1372w" width="860" height="82" /></a></p>
<p><a href="http://media.24joursdeweb.fr/2014/12/lequipe.jpg"><img alt="Chargement du site LEquipe.fr" src="http://media.24joursdeweb.fr/2014/12/lequipe-860x115.jpg" srcset="http://media.24joursdeweb.fr/2014/12/lequipe-860x115.jpg 860w, http://media.24joursdeweb.fr/2014/12/lequipe-430x57.jpg 430w, http://media.24joursdeweb.fr/2014/12/lequipe.jpg 1365w" width="860" height="115" /></a></p>
<p>Veuillez patienter, un opérateur va prendre votre appel</p>
<h2>L’arsenal technique</h2>
<p>Le problème est donc bien réel et s’inscrit dans la durée : les utilisateurs seront de plus en plus exigeants, les sites de plus en plus gourmands, et la latence des réseaux mobiles ne fera pas de grands progrès. Ce qui tombe bien c’est qu’on peut y faire quelque chose.</p>
<h3>Le chemin critique</h3>
<p>D’accord le chemin critique est <a href="https://developer.yahoo.com/performance/rules.html" hreflang="en">connu depuis 2006</a>, désolé de redire les choses mais sur mobile il ne faut surtout pas se rater sur les bases. Prenons un cas un peu extrême (iOS < 8, grosse latence, débit correct).</p>
<p><a href="http://media.24joursdeweb.fr/2014/12/lemonde-deroule.jpg"><img alt="Chargement du site LeMonde.fr" src="http://media.24joursdeweb.fr/2014/12/lemonde-deroule-860x128.jpg" srcset="http://media.24joursdeweb.fr/2014/12/lemonde-deroule-860x128.jpg 860w, http://media.24joursdeweb.fr/2014/12/lemonde-deroule-430x64.jpg 430w, http://media.24joursdeweb.fr/2014/12/lemonde-deroule.jpg 1575w" width="860" height="128" /></a></p>
<p>La fameuse angoisse de la page blanche (1 case = 1 seconde)</p>
<p>Pourquoi 9 secondes avant le premier pixel, et 10 avant de voir quelque chose d’utile ? Regardons l’action au ralenti, avec la trace réseau suivante.</p>
<p><a href="http://media.24joursdeweb.fr/2014/12/lemonde-reseau.jpg"><img alt="Vue réseau sur LeMonde.fr" src="http://media.24joursdeweb.fr/2014/12/lemonde-reseau.jpg" width="759" height="671" /></a></p>
<p>Le trait vert est le premier Graal.</p>
<p>Vous pouvez considérer que tout ce qui est à gauche de la ligne verticale verte (l’affichage du premier pixel) est sur le chemin critique. Un effort d’optimisation est déjà en place car les <abbr title="Cascading stylesheets, feuilles de styles">CSS</abbr> sont déjà groupés et <strong>les JavaScript sont chargés de manière asynchrone</strong>. Sauf que c’est à cause de cette dernière optimisation que le site est ralenti : ce qui marche sur navigateur de bureau n’est pas forcément vrai sur mobile.</p>
<p><strong>Déplacer les JavaScript en bas de page aurait aussi été une mauvaise idée</strong>, grâce à notre ami Safari iOS qui refuse d’afficher quoi que ce soit tant que ces fichiers ne sont pas là. Ici comme sur la plupart des sites, on gagnerait plusieurs secondes en chargeant de manière classique les <strong>fichiers <abbr title="Javascript">JS</abbr> agglomérés, en haut de page</strong>.</p>
<p>Pour résumer :</p>
<ul>
<li>testez sur les vraies plate-formes avant d’appliquer des recettes qui marchent ailleurs.</li>
<li>choisissez la bonne stratégie de chargement des <abbr>JS</abbr> : de manière classique en haut de page, ça marche souvent très bien</li>
<li>les recettes classiques doivent être appliquées : groupement en 6 requêtes maximum des <abbr>HTML</abbr> / <abbr>CSS</abbr> / <abbr>JS</abbr></li>
<li>minification et gzip des ressources de type texte</li>
</ul>
<h3>La police</h3>
<p>Les <i lang="en">fonts</i> sont la grosse <a href="http://httparchive.org/trends.php?s=All&minlabel=Nov+15+2011&maxlabel=Nov+15+2014#perFonts" hreflang="en">tendance 2013</a> et sont maintenant présentes sur la moitié des sites. C’est joli tout plein mais ça retarde encore le moment où l’utilisateur peut accéder au contenu. En fait il y a 3 états possibles pour l’affichage de votre texte.</p>
<p><a href="http://media.24joursdeweb.fr/2014/12/deroule-font-lequipe.jpg"><img alt="Chargement des polices sur LEquipe.fr" src="http://media.24joursdeweb.fr/2014/12/deroule-font-lequipe-860x364.jpg" srcset="http://media.24joursdeweb.fr/2014/12/deroule-font-lequipe-860x364.jpg 860w, http://media.24joursdeweb.fr/2014/12/deroule-font-lequipe-430x182.jpg 430w, http://media.24joursdeweb.fr/2014/12/deroule-font-lequipe.jpg 1180w" width="860" height="364" /></a></p>
<p>Les phases possibles pour un chargement de police</p>
<p><strong>Phase 1</strong> : pas de bras, pas de chocolat, le texte est là mais masqué. Un peu dommage pour un site essentiellement basé sur le contenu textuel.</p>
<p><strong>Phase 2</strong> : le navigateur en a marre et vous sauve la mise en affichant le texte. Mal stylé mais fonctionnel.</p>
<p><strong>Phase 3</strong> : tout va bien, le site est quasiment comme sur la maquette. On espère que l’utilisateur regarde encore.</p>
<h4>Savoir coder</h4>
<p>L’art ancestral de <strong>l’amélioration progressive </strong>doit aussi s’appliquer aux polices : la phase 2 n’existe que si vous avez bien pensé à préciser une police système de fallback. Dans le cas contraire <strong>l’utilisateur regardera plus longtemps le site sans rien pouvoir y lire</strong>.</p>
<p>Pour raccourcir voire éliminer la phase sans tete lisible, il s’agit de faire un travail approfondi de coopération intégrateur / designer : 1 ou 2 polices maximum, de moins de 40<abbr>Ko</abbr>, testées sur toutes les plate-formes (mention spéciale à Chrome sur XP)</p>
<p>Ensuite vient le travail d’optimisation :</p>
<ul>
<li>la <i lang="en">font</i> critique doit avoir sa<strong> place dans vos 6 premières requêtes</strong>.</li>
<li>chargement asynchrone des polices secondaires (variantes, titres invisibles, police d'icônes…).</li>
</ul>
<p>Il y a des techniques plus avancées permettant de toujours afficher du texte même si ça n’est pas la <i lang="en">font</i> finale, ce qui ne plaît pas toujours. Vous pouvez également utiliser les <code>data:uri</code> et un encodage base64 de votre police directement dans le <abbr>CSS</abbr>, mais vous l’alourdissez du poids de la police.</p>
<p>Testez.</p>
<h3>Se méfier des autres</h3>
<p>Je vois encore des développeurs inclure des scripts depuis des serveurs qui ne leur appartiennent pas. Il s’agit le plus souvent d’une version plus ou moins à jour de jQuery ou de <abbr>HTML</abbr>5 shim. Les bénéfices sont nuls, et le risque énorme. Vous pensiez sérieusement que les serveurs des grands de ce monde sont disponibles 100% du temps ? Ou qu’ils répondent toujours plus vite que vos propres serveurs ?</p>
<p>Non.</p>
<p><a href="http://media.24joursdeweb.fr/2014/12/spof-facebook.jpg"><img alt="Single Point Of Failure Facebook" src="http://media.24joursdeweb.fr/2014/12/spof-facebook.jpg" width="662" height="293" /></a></p>
<p><abbr title="Single point of failure, point unique d'échec">SPOF</abbr> ? pouf.</p>
<p>Sur ces graphes, on voit les temps d’affichage de sites majeurs (ligne du haut) <strong>augmenter de 20 secondes</strong> pendant le temps où les serveurs Facebook sont moins disponibles. Pour faire le point des dangers page par page, je vous conseille d’utiliser <a href="https://chrome.google.com/webstore/detail/spof-o-matic/plikhggfbplemddobondkeogomgoodeg" hreflang="en">SPOF-O-Matic</a>.</p>
<p>Sur mobile la résolution du nouveau nom de domaine est particulièrement pénible à cause de la latence. Les stratégies sont relativement simples, en fonction de la nature de la ressource :</p>
<ul>
<li><i lang="en">widget</i> / bouton : utiliser la version asynchrone pour ne pas dépendre des autres,</li>
<li><i lang="en">widget</i> / bouton : mieux, une version statique vous évite 100-200<abbr>Ko</abbr> et des temps d’exécution énormes,</li>
<li>JavaScript ou <abbr>CSS</abbr> tiers : <strong>rapatriez le script bien au chaud chez vous, groupez-le avec les autres si il est critique</strong></li>
<li>polices : rapatriement sur vos serveurs, quitte à payer une licence</li>
</ul>
<h4>Publicités, trackers et autres <i lang="en">A/B testing</i></h4>
<p>Ils requièrent tous d’être en haut de page pour être exécutés en premier et sont massivement déployés sur les sites même dédiés mobiles. Le département marketing moderne se doit de posséder au moins un exemplaire de chaque catégorie.</p>
<p><img alt="Poignée de main" src="http://media.24joursdeweb.fr/2014/12/poignee-de-main-383x430.jpg" srcset="http://media.24joursdeweb.fr/2014/12/poignee-de-main-383x430.jpg 383w, http://media.24joursdeweb.fr/2014/12/poignee-de-main.jpg 663w" width="383" height="430" /></p>
<p>« J’ai dépassé mon objectif de 42 régies pub sur la home, et toi ?
— Je viens de découvrir 4 fournisseurs d’<i lang="en">A/B testing</i>, ça part en prod demain »</p>
<p>Les solutions techniques non bloquantes sont à négocier d’abord en interne pour faire prendre conscience du problème, puis auprès des fournisseurs, de moins en moins réfractaires. On arrive fréquemment à mettre les publicités dans des <i lang="en">iframes</i> et les scripts de <i lang="en">tracking</i> en asynchrone. Pour l’<i lang="en">A/B testing</i> ou certaines zones de publicité vitales, une bonne option, techniquement complexe, consiste à rapatrier en local, de préférence de manière automatisée, le code <abbr>JS</abbr> du prestataire, puis à l’inclure comme le reste des <abbr>JS</abbr> critiques en haut de page.</p>
<h3>Les 3 caches</h3>
<p>C’est comme les 3 coquillages : on peut faire sans, mais les gens du futur se moqueront. Partant du principe qu’il n’y a pas plus rapide qu’une requête qu’on ne fait pas, il convient de maîtriser rapidement les 3 techniques suivantes sur mobile.</p>
<h4>Le cache <abbr title="Hypertext transfer protocol, Protocole de transfert hypertexte">HTTP</abbr></h4>
<p>Depuis toujours la recette est simple :</p>
<ul>
<li>mettre des temps de cache très longs sur toutes les ressources statiques (plus d’1 mois),</li>
<li>en cas de mise en production d’une nouvelle version, la mise à jour des caches clients se fait par le changement des <abbr title="Universal resource locator, emplacement de ressource unique">URL</abbr>s pointant sur les statiques.</li>
</ul>
<p>Point.</p>
<p>Sur navigateurs de bureau, on pourrait s’arrêter là mais sur mobile le cache <abbr>HTTP</abbr>peut se révéler très volatile, voire capricieux. Si l’<abbr title="Operating system, Système d'exploitation">OS</abbr> estime qu’il a besoin de mémoire, il peut vider le cache du navigateur. C’est la raison pour laquelle certains sites ont développé leur propre système de mise en cache, en se basant sur <i lang="en">localStorage</i>.</p>
<h4><i lang="en">DOM Storage</i></h4>
<p><a href="https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/Storage" hreflang="en"><i lang="en">localStorage</i></a> est un système simplissime de stockage de clé / valeur, de 5<abbr>Mo</abbr>minimum. Pour limiter les accès au serveur, stockez-y des informations aussi simples qu’un historique de navigation ou lourdes et complexes, comme une liste de toutes les marques automobile et des modèles. L’utilisation en est tellement facile que c’en est ennuyeux.</p>
<p>Du coup certains en abusent.</p>
<p><a href="http://media.24joursdeweb.fr/2014/12/localstorage-google.jpg"><img alt="localStorage sur Google" src="http://media.24joursdeweb.fr/2014/12/localstorage-google.jpg" width="795" height="320" /></a></p>
<p>Tiens, du <abbr>CSS</abbr> dans mon <i lang="en">localStorage</i></p>
<p>Certains sites stockent carrément du <abbr>HTML</abbr>, du <abbr>CSS</abbr>, du JavaScript, des images ou des <i lang="en">fonts</i> puis via un système classique de gestion de session évitent de renvoyer les ressources au client.</p>
<h4><i lang="en">Application Cache</i> (dit <i lang="en">offline</i>)</h4>
<p>L’énorme avantage ergonomique d’une application native installée chez l’utilisateur, c’est qu’elle va s’ouvrir quand vous appuyez dessus, même si à ce moment-là vous transitez par le tunnel sous la manche. Imaginez que l’on puisse faire de même avec nos sites : il suffirait d’aller une seule fois sur une des pages pour rapatrier l’ensemble de l’interface, et toute la navigation se ferait en local. Les mises à jour du contenu ne se feraient que lorsque la connexion est rétablie et on ne dépendrait pas de la validation d’un <i lang="en">store</i> pour mettre à jour son application.</p>
<p>Figurez-vous que la <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Using_the_application_cache" hreflang="en">technologie existe</a>, et date même d’une époque lointaine où Apple ne pensait pas encore à l’<i lang="en">app store</i> et à Objective C pour écrire des applications mais bien au Web et ses langages . Faites-moi plaisir et enfourchez votre mobile pour aller sur l’<abbr>URL</abbr> suivante :</p>
<p><a href="http://appcache.offline.technology/demo/" hreflang="en">http://appcache.offline.technology/demo/</a></p>
<p>Une fois la page chargée complètement (scrollez en bas pour voir les <i lang="en">logs</i>), <strong>passez <i lang="en">offline</i></strong> (mode avion), puis allez sur la 2de page nommée cache. Non seulement la page s’affiche comme si de rien n’était mais l’image du machin vert est également présente alors qu’elle ne peut pas être dans le cache HTTP classique.</p>
<p><img alt="Belle bête" src="http://media.24joursdeweb.fr/2014/12/tbrun5-430x312.png" srcset="http://media.24joursdeweb.fr/2014/12/tbrun5-430x312.png 430w, http://media.24joursdeweb.fr/2014/12/tbrun5.png 767w" width="430" height="312" /></p>
<p>Belle bête</p>
<p>Si vous utilisez Chrome, vous pouvez regarder sur <kbd>chrome://appcache-internals/</kbd>votre liste des sites utilisant cette technologie.</p>
<p>À ce stade de la compétition vous avez peut être déjà entendu dire <a href="http://alistapart.com/article/application-cache-is-a-douchebag" hreflang="en">beaucoup de mal</a>sur appCache. C’est effectivement le genre d’<abbr title="Interface de programmation applicative">API</abbr> qui demande du temps et de l’amour pour être bien comprise, mais elle apporte un énorme confort utilisateur et est très bien <a href="http://caniuse.com/#feat=offline-apps" hreflang="en">supportée</a> sur mobile et même sur bureau. Elle est très adapté aux sites qui se comportent comme des applications (une seule page) mais il existe des techniques à base d’<i lang="en">iframe</i> pour partager ce super cache entre plusieurs pages d’un site plus classique.</p>
<h3>Les images</h3>
<p>Elles représentent fréquemment les deux tiers du poids des sites et ralentissent le chargement des ressources critiques.</p>
<h4>Éviter de les charger</h4>
<p>Captain Obvious a dit : c’est plus rapide sans image. C’est pas faux et il commence à y avoir pas mal de techniques permettant d’éviter des requêtes :</p>
<ul>
<li><abbr>CSS</abbr>3 pour les dégradés, arrondis, rotations et opacités. Dommage que ça ne soit plus à la mode.</li>
<li>Les caractères unicodes des polices fournies avec les <abbr>OS</abbr> : ►★✓ ⇨ (attention si vous visez également <abbr>IE</abbr>8 sur Windows XP)</li>
</ul>
<p>Lorsque certaines images <strong>sont critiques pour votre interface</strong>, vous pouvez les embarquer directement dans le <abbr>HTML</abbr> ou le <abbr>CSS</abbr>. C’est la technique du <a href="http://css-tricks.com/data-uris/" hreflang="en"><code lang="en">data:uri</code>couplé à l’encodage en base64</a>. Oui ça a l’air sale mais uniquement si c’est mal fait.</p>
<p>L’image la plus importante à charger est celle indiquant justement qu’il y a chargement ! Pourquoi ne pas mettre également le logo ou une image de contenu réellement importante.</p>
<p><a href="http://media.24joursdeweb.fr/2014/12/loading-pmvc.jpg"><img alt="Chargement du site Promovacances" src="http://media.24joursdeweb.fr/2014/12/loading-pmvc-860x127.jpg" srcset="http://media.24joursdeweb.fr/2014/12/loading-pmvc-860x127.jpg 860w, http://media.24joursdeweb.fr/2014/12/loading-pmvc-430x63.jpg 430w, http://media.24joursdeweb.fr/2014/12/loading-pmvc.jpg 1500w" width="860" height="127" /></a></p>
<p>L’image d’attente arrive bien avant toutes les autres</p>
<p>Il ne faut pas abuser de la technique car elle alourdit le <abbr>HTML</abbr> et le <abbr>CSS</abbr> qui sont des ressource critiques, mais sur quelques petites images stratégiques cela fait patienter l’utilisateur.</p>
<h4>Chargement à la demande</h4>
<p>C’est à se demander pourquoi les navigateurs ne le font pas déjà d’eux-mêmes : l’idée est bêtement de <strong>ne pas charger les images qui ne sont pas visibles </strong>! C’est radical pour les sites avec beaucoup d’images car cela libère la bande passante pour les ressources critiques et les images qui sont visibles. Voici un <a href="https://github.com/vvo/lazyload" hreflang="en">bon exemple de librairie</a>, utilisé notamment sur lequipe.fr, bureau ou mobile. Naviguez dessus (c’est pour le travail) et scrollez très vite : vous verrez les images apparaître au fur et à mesure de votre progression dans la page.</p>
<h3>Cette technique seule sauve des dauphins tous les jours.</h3>
<h3>Les images <i lang="en">responsive</i></h3>
<p>Comment servir le meilleur rapport poids / qualité à l’utilisateur mobile ?</p>
<h4>Le standard</h4>
<p>Votre plus grand problème concernant les images à délivrer sur mobile est de comprendre ce que vous voulez réellement en faire. Le problème est de définir le problème si vous voulez, en répondant à ces 4 questions :</p>
<ul>
<li>adaptation à la taille du <i lang="en">viewport</i> : veut on servir des petites images aux petits écrans ?</li>
<li>écran haute densité de pixels (ok, retina©) : veut-on leur servir des images de très haute qualité ?</li>
<li><i lang="en">art direction</i> : pourquoi ne pas afficher des images au cadrage différent en fonction de la taille de l’écran ?</li>
<li>format de l’image : veut-on servir du WebP à Chrome, du <abbr>JPG</abbr> <abbr>XR</abbr> à windows et du <abbr>JPG</abbr> 2000 à iOS ?</li>
</ul>
<p>Pour compliquer l’affaire, sachez que :</p>
<ul>
<li>un écran retina peut avoir un petit <i lang="en">viewport</i>,</li>
<li>deviner la bande passante de l’utilisateur est hautement hasardeux,</li>
<li>vous ne connaissez pas la seule valeur qui compte : la taille physique de l’écran et la distance écran-œil.</li>
</ul>
<p>Si vous êtes capable d’écrire un cahier des charges répondant à ces questions, alors vous pouvez vous pencher sur la réponse officielle et standard au problème : <<a href="https://dev.opera.com/articles/responsive-images/" hreflang="en"><code lang="en">picture</code></a>>. Le temps que ça marche partout, la librairie <abbr>JS</abbr> officielle est <a href="https://github.com/scottjehl/picturefill" hreflang="en">picturefill 2.0</a>.</p>
<p><img alt="La balise <picture>" src="http://media.24joursdeweb.fr/2014/12/picture-430x257.jpg" srcset="http://media.24joursdeweb.fr/2014/12/picture-430x257.jpg 430w, http://media.24joursdeweb.fr/2014/12/picture-860x514.jpg 860w, http://media.24joursdeweb.fr/2014/12/picture.jpg 911w" width="430" height="257" /></p>
<p>La question est complexe, la réponse aussi.</p>
<p>La peinture étant un peu fraîche, il n’y a pour l’instant pas de retour sur la mise en production de cette technique. L’amélioration de performances devra donc se tester chez vous, en particulier sur les plateformes sans <a href="http://caniuse.com/#search=picture">support</a>.</p>
<h4>Fait à la main, roulé sous les aisselles</h4>
<p>Difficile pour une solution générique de faire mieux que du code écrit spécifiquement. À force j’utilise généralement chez mes clients un petit ensemble de techniques :</p>
<ul>
<li><abbr>JPG</abbr> grande résolution, qualité 0 : pour une image unique à faire passer sur tous type d’écran, éditée à la main. Ça passe pour 90% des <abbr>JPG</abbr>. (<a href="http://bit.ly/jpg-0">à essayer</a> sur un écran haute densité),</li>
<li>images basse définition pour remplir l’espace, suivies du chargement de la haute définition. Le <abbr>JPG</abbr> progressif marchant mal sur iOS, c’est une manière d’avoir le même effet,</li>
<li><i lang="en">lazy-loading</i> pour la majorité des images, images embarquées pour les critiques,</li>
<li>quand on peut, des formats vectoriels : <abbr title="Scalable vector graphics, images vectorielles scalables">SVG</abbr> et polices d’icônes.</li>
</ul>
<p>Combinez, testez et saupoudrez de <abbr>JS</abbr> pour obtenir de beaux effets. La maintenance n’est pas facile mais les résultats sont bons.</p>
<h3>Interfaces fluides</h3>
<p>Vous avez remarqué que les smartphones sont capables de jouer de manière fluide des jeux 3D mais ont des difficultés dès qu’il s’agit de retailler des <code>div</code>s ou de jouer avec du texte ? Au contraire de la 3D, il n’existe pas de puce dédiée à recalculer un <abbr title="Document object model, Modèle d'ojet document">DOM</abbr> qui bouge ou l’écoulement des caractères. Il va donc falloir aider un peu nos pages.</p>
<h4>Animations</h4>
<p>Elles peuvent être fluides si on les travaille au corps. D’abord il y a un certain nombre de propriétés CSS qu’il vaut mieux ne pas animer comme <i lang="en">top</i>, <i lang="en">left</i>, <i lang="en">width</i> et <i lang="en">height</i>. On leur préférera les variations de <i lang="en">transforms</i> comme <i lang="en">translateX()</i> ou <i lang="en">scale()</i> qui ont la bonne idée d’être pris en charge directement au niveau du processeur graphique.</p>
<p>Vous pouvez essayer dans l’ordre :</p>
<ul>
<li>les <a href="https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Using_CSS_transitions" hreflang="en">transitions <abbr>CSS</abbr></a>, qui suffisent à la plupart des sites,</li>
<li>les <a href="https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Using_CSS_animations" hreflang="en">animations <abbr>CSS</abbr></a>, quipermettent des effets plus avancés</li>
<li>enfin si <abbr>JS</abbr> doit tout piloter (un jeu par exemple), utilisez <code lang="en">requestAnimationFrame</code>, <strong>arrêtez jQuery animate</strong> et préférez des librairies spécialisées comme D3.js, GSAP ou TweenJS qui génèrent du <abbr>CSS</abbr>3.</li>
</ul>
<h4><abbr>CSS</abbr> 3 avec modération</h4>
<p>Remplacer les images de décoration par <abbr>CSS</abbr> c’était une bonne idée jusqu’à ce que vous réalisiez que le scroll lag sérieusement depuis que vous avez rajouté une ombre portée sur toute la hauteur de votre liste. De fait, tous les effets d’ombre, de transparence et de coins arrondis sont calculés en continu et cela peut coûter cher.</p>
<p>Une seule règle : tester, et sur de vrais mobiles bien sûr.</p>
<h4>Calculs</h4>
<p>On ne calcule pas tous les jours une suite de Fibonacci mais si ça vous arrive, utilisez les <i lang="en">Web Workers</i>. Sur les mobiles modernes ça vous donne accès à un second processeur pour exécuter du JavaScript. Sur les autres, la bonne veille technique du <code lang="en">setTimeout(0)</code> reste valable pour exécuter des boucles lourdes sans bloquer l’interface. Devinez qui vous a écrit <a href="https://gist.github.com/jpvincent/867010d224d61a6539d3">un script</a> pour ça ?</p>
<h4>Testez !</h4>
<p>Les derniers navigateurs mobiles acceptent enfin le déboguage à distance ! Cela signifie que vous allez pouvoir utiliser le <i lang="en">profiler</i> que vous connaissez sur de vrais téléphones et débusquer les endroits qui font ramer votre interface.</p>
<p><a href="http://media.24joursdeweb.fr/2014/12/profiler.jpg"><img alt="Vue réseau" src="http://media.24joursdeweb.fr/2014/12/profiler.jpg" width="717" height="318" /></a></p>
<p>Oh la belle verte : c’est mon <abbr>CSS</abbr> qui est gourmand</p>
<p>Pour les anciens <abbr>OS</abbr> comme Android 2.3, cela reste la devinette, rassurez vous.</p>
<h2>Impliquer, mesurer, surveiller</h2>
<p>On ne peut pas résoudre le problème qu’on ne voit pas. Avant de commencer tout projet de performance, il faut <strong>définir</strong> ce que doit être la performance, prendre un point de départ, choisir ses métriques, et surveiller automatiquement les performances dans le temps.</p>
<p>Sinon le projet performance s’arrêtera après la mise en prod des premières améliorations de performance.</p>
<p>Il fut un temps où il était possible d’avoir une équipe Web composée d’une seule personne : référencement, accessibilité, code, design, administration système, c’était fun. Mais aujourd’hui, à toutes vos compétences techniques il faut maintenant rajouter une grosse capacité à comprendre pourquoi l’on code et comment dialoguer avec le reste de la boite.</p>
<p>Donc par pitié, ne partez pas en croisade solitaire contre les lenteurs de votre site, au risque que personne dans la boîte ne s’en aperçoive. Il faut d’abord sensibiliser et évangéliser sur le coût du manque de performance pour la société et l’image du produit. Toute l’introduction de cet article est là pour cela.</p>
<p>Une fois que vous avez l’oreille des décideurs, faites écrire dans ce qui se rapproche le plus d’une spécification les objectifs de performance : en dessous de quels temps considère-t-on qu’il faut agir ? Vous pouvez par exemple prendre ces chiffres, relativement universels sans être trop ambitieux :</p>
<ol>
<li>Pour 80% des utilisateurs</li>
<li>Premier rendu en moins de 2 secondes</li>
<li>Fonctionnalité principale en moins de 5 secondes</li>
<li>Navigation de page en page en moins de 2 secondes</li>
</ol>
<h3>Les utilisateurs et le premier rendu</h3>
<p>Les deux premiers points demandent à déterminer l’équipement des utilisateurs :</p>
<ul>
<li>Quels navigateurs ?</li>
<li>Quelle est la puissance des mobiles ?</li>
<li>Quelle est la qualité de leur connexion ? (l’origine géographique peut suffire à la deviner)</li>
</ul>
<p>Les seuls moments où on vous a dit que le site était lent, c’est lorsque vos collaborateurs ont essayé de l’utiliser sans le Wifi du bureau. Comme des vrais gens donc. Il faut reproduire ces conditions de manière systématique et certaine pour constater les dégâts et juger des progrès avec le temps.</p>
<p>Le temps de premier rendu est relativement facile à vérifier avec des outils comme WebPagetest.org. Ce qui est plus compliqué c’est de paramétrer WebPagetest avec une simulation de connexion qui soit réaliste par rapport à vos utilisateurs. Comme ce n’est pas le cas de Webpagetest.org par défaut, je vous donne mes paramètres beaucoup plus représentatifs de la France.</p>
<p><a href="http://media.24joursdeweb.fr/2014/12/connexion-webpagetest.png"><img alt="Connexions WebPageTest" src="http://media.24joursdeweb.fr/2014/12/connexion-webpagetest.png" width="462" height="91" /></a></p>
<p>Des paramètres de connexion plus réalistes.</p>
<p>WebPagetest s’installe également en interne, et peut <a href="https://code.google.com/p/mobitest-agent/" hreflang="en">piloter des mobiles</a>.</p>
<h3>La fonctionnalité principale</h3>
<p>Là ça se complique : comment <strong>déterminer le temps d’accès à la fonctionnalité principale</strong> ? Aucun outil ne peut faire cela automatiquement pour vous car seul vous connaissez bien votre interface (prends ça <a href="http://en.wikipedia.org/wiki/Skynet_%28Terminator%29" hreflang="en">Skynet</a>). Par contre vous pouvez collecter automatiquement des temps en JavaScript et vous les envoyer dans l’outil de tracking qui vous sied (G. Analytics peut suffire au début).</p>
<p>Si vous êtes un site de news ou à contenu visuel, trackez la fin de téléchargement de la première image visible utile par exemple (tiens, <a href="https://github.com/jpvincent/requestTracker">voici du code</a> qui peut vous y aider). Si votre fonctionnalité principale dépend de JavaScript (vidéo, moteurs de recherche complexes, application…), il est encore plus facile de minuter le moment où le contenu devient interactif.</p>
<h3>La navigation interne</h3>
<p>Le dernier point consiste à <strong>ne pas décevoir l’utilisateur après la première page</strong>, que ce soit la vitesse de chargement des pages suivantes ou la fluidité de l’interface.</p>
<p>Là vous pouvez surveiller les taux de mise en cache client (utilisation avancée de <a href="http://fr.slideshare.net/jpvincent/le-monitoring-de-la-performance-front">WebPagetest Monitor</a>) et simuler des navigations dans vos tests de performance.</p>
<p><a href="http://media.24joursdeweb.fr/2014/12/cache-F.png"><img alt="Note de F en Cache Static Content" src="http://media.24joursdeweb.fr/2014/12/cache-F.png" width="59" height="93" /></a></p>
<p>Attention chérie ça va ramer</p>
<p>Il n’y a pas de moyen facile et automatique de surveiller que l’interface elle-même est fluide sur mobile : contentez-vous d’une vérification systématique et digitale.</p>
<h2>Stratégies de chargement</h2>
<p>Il faut jouer avec la perception utilisateur et afficher quelque chose d’utile le plus rapidement possible. Cela demande à s’asseoir autour d’un café avec les autres équipes et à écrire la liste des priorités d’une page. Charge à vous de traduire cela en code pour régler l’ordre dans lequel les ressources vont être chargées par le navigateur.</p>
<p>Prenons 2 exemples de sites qui ont fait ce travail de priorité des requêtes.</p>
<h3>Home Google</h3>
<p>La fonctionnalité principale est évidemment l’affichage du champ texte. Mais les fonctionnalités secondaires sont légion : <i lang="en">autocomplete</i> sur ce champ, l’obligatoire intégration Google+, le menu, la suggestion de géolocalisation et même une bannière d’auto-promotion.</p>
<p><a href="http://media.24joursdeweb.fr/2014/12/hp-google.jpg"><img alt="Home de Google" src="http://media.24joursdeweb.fr/2014/12/hp-google.jpg" width="241" height="428" /></a></p>
<p>Ils ont de l’avenir</p>
<p>Google n’attend pas : il embarque un <abbr>CSS</abbr> minimaliste et suffisant dans le <abbr>HTML</abbr>, le <abbr>HTML</abbr> ne contient que le champ et des éléments pour définir la structure de la page. <strong>En 1 requête et 22<abbr>Ko</abbr>, soit l’équivalent de 2 allers-retours réseau, la fonctionnalité principale est déjà utilisable</strong>. Ensuite arrivent plus de 200<abbr>Ko</abbr> de ressources diverses pour afficher et rendre fonctionnel le reste de la page.</p>
<h3>Home avec carrousel massif</h3>
<p>Cette homepage était principalement vue depuis la Chine, il a donc fallu la calibrer pour des petits débits / grosses latences : elle était déjà optimisée pour le mobile. Par contre il fallait également afficher dans un carrousel plusieurs images de haute qualité de 1200 pixels de large.</p>
<p><a href="http://media.24joursdeweb.fr/2014/12/hp-cma.jpg"><img alt="Home de CMA" src="http://media.24joursdeweb.fr/2014/12/hp-cma.jpg" width="236" height="344" /></a></p>
<p>Le carrousel classique</p>
<p>Du point de vue du réseau, charger plusieurs ressources en parallèle est généralement vu comme une bonne idée. Sauf lorsque le débit est ténu et les ressource trop grosses comme c’est le cas ici. Le chargement de plusieurs images en simultané ralentissait le chargement des ressources du chemin critique.</p>
<p>Il a donc fallu prioriser : logo, texte, et image principale d’abord.</p>
<p>Les fonctionnalités secondaires sont les icônes, un moteur de recherche (non visible ici) les <strong>autres images du carrousel</strong> et des images bien plus bas.</p>
<p>Adieu donc la police, utilisation de <code>data:uri</code> pour embarquer le logo en base64, inclusion en <code><img src></code> traditionnel de la première image du carrousel (et seulement elle) et on développe un petit script pour <strong>aller chercher de manière asynchrone les photos suivantes</strong> avant de vraiment démarrer le carrousel.</p>
<h2>Conclusion : <i lang="en">Mobile first</i></h2>
<p>Ça n’est pas du lâcher de <i lang="en">buzzword</i> gratuit mais bien une adaptation à la manière dont nos utilisateurs utilisent nos sites, qu’ils soient en situation de mobilité, dans un pays lointain ou qu’ils fassent partie des 20% de français avec moins de 2<abbr>Mb</abbr>/<abbr>s</abbr>.</p>
<p>Le <i lang="en">mobile first</i> est l’héritier de la pensée « amélioration progressive » : délivrons très rapidement la promesse initiale, chargeons les fonctionnalité supplémentaires après et en fonction des capacités du client. Cela oblige à se poser la vraie question : quelles sont les priorités de chaque page ?</p>
<p>Cela s’intègre parfaitement avec un processus de conception moderne où designer et intégrateur web passent pas mal de temps l’un à côté de l’autre pour fignoler les maquettes sur les mobiles.</p>
<p>Nous avons évoqué une palanquée de techniques : certaines sont connues depuis bientôt 10 ans, beaucoup émergent et certaines sont devenues dangereuses. Toutes répondent à une situation particulière donc c’est à vous de <strong>tester ce qui marche ou pas</strong>, page à page.</p>
<p>Le processus de travail est presque aussi important que les techniques individuelles : la communication et l’effort de groupe sont vitaux pour maintenir la qualité à travers le temps. Sans monitoring ou automatisation des déploiements cela va rester amateur et pénible. Sans soutien hiérarchique du client (interne, externe, hiérarchique, bref celui qui vous paye) vous n’irez pas bien loin seul. Ou pire vous serez frustré par ce métier pourtant formidable qui est le nôtre.</p>Cet article a été rédigé initialement pour 24 jours de web : http://www.24joursdeweb.fr/2014/le-web-mobile-et-la-performance/Quel est le débit moyen en France ?2012-11-13T15:04:21+00:002012-11-13T15:04:21+00:00/2012/11/13/quel-est-le-debit-moyen-en-france<p>À quelle vitesse s’affiche un site ? La question est importante tant le confort d’utilisation et les revenus d’un site peuvent dépendre de cette réponse. Vous intégrez probablement déjà des pratiques visant à améliorer le rendu des pages et leur réactivité, et si vous êtes en phase d’industrialisation, vous faites peut-être du monitoring de la performance. Au moment de paramétrer votre solution, une question a dû vous poser problème : quelle est la connexion type en France ? Et pour les mobiles ?</p>
<p>Nous allons non seulement répondre à ces deux questions, mais en plus nous apercevoir qu’il y a une question plus pertinente à se poser.</p>
<h2>Pourquoi et comment calibrer ses test ?</h2>
<p>Les solutions de <em>monitoring</em> du marché testent les pages généralement sans vous dire avec quel navigateur (probablement des <em>Webkit headless</em>, pour accélérer les tests), et le font depuis des <em>datacenters</em> qui sont beaucoup trop proches des nœuds principaux du Web. Potentiellement, les sondes sont exécutées à partir de machines hébergées physiquement dans le même <em>datacenter</em> que votre hébergeur ou avec un <em>peering</em> privilégié avec le CDN.</p>
<p>Autant dire que les tests standard ne sont pas représentatifs de ce qu’endurent vos utilisateurs et c’est la raison pour laquelle on limite volontairement débit et latence, par exemple sur l’indispensable <a href="http://www.webpagetest.org/">WebPageTest</a> ou chez certaines solutions payantes. WebPageTest a le mérite d’être transparent car vous avez une liste de connexions pré-paramétrées cependant nous allons voir qu’elles ne représentent pas du tout la France.</p>
<p>[caption id=”attachment_1142” align=”alignnone” width=”455”]<a href="http://braincracking.org/wp-content/uploads/2012/10/paramètre-WPT-org.png"><img class="size-full wp-image-1142 " title="paramètre WPT org" src="http://braincracking.org/wp-content/uploads/2012/10/paramètre-WPT-org.png" alt="" width="455" height="140" /></a> Paramètres par défaut de WebPageTest.org[/caption]</p>
<p>Côté transit réseau, il y a essentiellement 4 données qui influencent l’affichage d’un site :</p>
<ul>
<li>la latence ou ping, qui est le temps d'un aller / retour entre un navigateur et le serveur qui lui répond. Pour le <a href="http://httparchive.org/trends.php">site moyen</a>, en HTTP 1.1, basé sur TCP, c'est <a href="http://performance.survol.fr/2008/03/impact-de-la-latence-reseau/">la mesure la plus importante</a>. C'est aussi la plus difficile à obtenir.</li>
<li>le download ou bande passante descendante, qui reste importante pour récupérer de gros objets. On nous vend des ADSL à plus de 20Mb/s (<a href="http://adsl.sfr.fr/toutes-nos-offres/">1</a>, <a href="http://abonnez-vous.orange.fr/residentiel/accueil/accueil.aspx">2</a>, <a href="http://www.free.fr/adsl/internet.html">3</a> et des mobiles à 5Mb/s, qu'en est il vraiment ?</li>
<li>l'upload qui est rarement pertinent dans la webperf.</li>
<li>la perte de paquets : pratiquement inexistante sur des connexion filaires, mais omniprésent sur mobile. Là par contre impossible de trouver des chiffres, en supposant qu'ils signifient quelque chose</li>
</ul>
<p>Idéalement, il faudrait récupérer ces métriques de vos propres utilisateurs, grâce à du <a href="http://yahoo.github.com/boomerang/doc/use-cases.html">Real User Monitoring</a> (RUM), mais la mise en œuvre est moins aisée que de récupérer les informations de sources externes, telle que nous allons le faire ici.</p>
<p>Si vous êtes pressé ou si vous me faîtes aveuglément confiance (et je vous comprends), vous pouvez sauter directement à <a href="#resume">“En résumé”</a></p>
<h2>à la pêche aux chiffres</h2>
<p>En cherchant <a href="http://delicious.com/braincracking">dans mes archives</a> et sur Google, je me suis rendu compte qu’il existait très peu de rapports rendus publics. J’en liste ici 6 en extrayant des données brutes. N’hésitez pas à m’en indiquer d’autres ou à décortiquer plus avant les sources.</p>
<h3><a href="http://royal.pingdom.com/2010/11/12/real-connection-speeds-for-internet-users-across-the-world/">Etude Akamai / Pingdom de 2010</a></h3>
<p>Akamai est le leader mondial des CDNs, autant dire qu’ils voient passer du traffic et ont une bonne idée de ce qui se fait à l’échelle mondiale. L’étude de cette année là comprend la répartition des débits en France.</p>
<ul>
<li>Débit moyen : 3,3 Mb/s</li>
<li>Répartition :
<ul>
<li>25 % moins de 2 Mb/s</li>
<li>60 % entre 2 et 5 Mb/s</li>
<li>15 % plus de 5 Mb/s</li>
</ul>
</li>
</ul>
<h3><a href="http://www.akamai.com/stateoftheinternet/">Akamai 2012 </a></h3>
<p>Chiffres plus récents, mais dont l’échelle de répartition a changé.</p>
<ul>
<li>Débit moyen : 4,6Mb/s</li>
<li>Débit moyen des réseaux mobiles : 2,8Mb/s</li>
<li>Répartition :
<ul>
<li>0,1% moins de 256Kb/s</li>
<li>45% plus de 4Mb/s</li>
<li>4% plus de 10Mb/s</li>
</ul>
</li>
</ul>
<h3>DegroupTest, <a href="http://www.degrouptest.com/publications/6/barometre-debit-internet-s1-2012/1">2012</a> et <a href="http://www.degrouptest.com/publications/5/barometre-debit-internet-s2-2011/6">2011</a></h3>
<p>La population des utilisateurs de Degrouptest est probablement biaisée, car on a affaire à des gens qui au minimum s’inquiètent de leur débit. Ce qui exclut ma mère et probablement la votre. Mais ils ont l’avantage de mesurer le ping, et d’isoler les extrêmes comme la fibre, le mobile et l’outre-Mer.</p>
<ul>
<li>Débit moyen : 8,27 Mb/s (5,4Mb/s hors fibre )</li>
<li>Débit moyen des réseaux mobiles : 2.5Mbps (chiffres 2011)</li>
<li>Ping moyen : 80ms (86ms hors fibre)</li>
<li>Ping moyen réseaux mobiles : 200ms</li>
<li>Débit montant moyen : 1,3 Mb/s ( 641Kbp/s hors fibre)</li>
<li>Répartition :
<ul>
<li>13% à 27Mb/s de débit moyen</li>
<li>87% à 5,4Mb/s</li>
</ul>
</li>
<li>à noter :
<ul>
<li>Meilleur latence possible : Fibre Orange avec 19ms. Les opérateurs ADSL se situent entre 70ms (Free) et 100ms (SFR)</li>
<li>1s-1.5s de latence sur satellite</li>
<li>270ms de latence depuis l'outre-mer, 3Mbps de débit moyen</li>
</ul>
</li>
</ul>
<p>A noter que le débit et la latence moyenne ont baissé entre 2011 et 2012. J’imagine que leur public a du s’élargir à des zones moins bien couvertes.</p>
<h3><a href="http://www.60millions-mag.com/actualites/archives/la_verite_sur_le_debit_des_connexions_internet">60 millions de consommateurs, 2012</a></h3>
<p>Leur testeur de débit a tourné pendant 1 an en 2011 et leur public est probablement plus proche du français moyen que DegroupTest. En plus on y quantifie les variations périodiques de débit.</p>
<ul>
<li>Débit moyen : 5.6Mb/s</li>
<li>Répartition :
<ul>
<li>11.8% moins de 1Mb/s</li>
<li>12.8% de 1 à 2Mb/s</li>
<li>31,8% de 2 à 5Mb/s</li>
<li>30% de 5 à 10Mb/s</li>
<li>12% plus de 10Mb/s</li>
</ul>
</li>
<li>A noter :
<ul>
<li>-20% de débit à 21h</li>
<li>-10% le dimanche</li>
</ul>
</li>
</ul>
<h3><a href="http://www.arcep.fr/fileadmin/reprise/observatoire/4-2011/obs-marches-t4-2011.pdf">Etude ARCEP</a></h3>
<p>De cette étude sur les chiffres d’affaire des FAI et opérateurs mobile, j’ai extrait les volumes de population pour confirmer ou requalifier les chiffres des autres :</p>
<ul>
<li>22 millions d'abonnements ADSL et fibre</li>
<li>0,6 million d'abonnement fibre / cable</li>
<li>0,3 million abonnés RTC</li>
<li>32 millions d'abonnés mobile "data"</li>
<li>+80% de volume de data mobile par an</li>
</ul>
<h3><a href="http://www.cedexis.com/fr/country-reports/">Statistiques Cedexis</a></h3>
<p>Les statistiques de “l’aiguilleur du net” mesurent le temps d’aller-retour moyen entre un utilisateur de FAI et un CDN afin de choisir le meilleur. Les valeurs de latence y sont plus hautes qu’ailleurs car les CDN ne font pas que servir des fichiers statiques, il y a beaucoup d’intelligence lorsqu’une requête arrive chez eux. Je les prends car elles donnent une bonne idée de la vitesse avec laquelle les fichiers arrivent chez l’internaute qui visite des sites utilisant les CDN.</p>
<ul>
<li>118 ms de latence pour des fichiers hébergés sur Akamai (leader)</li>
<li>134 ms de latence pour des fichiers sur Amazon EC2 Europe</li>
</ul>
<p>Après les chiffres bruts, passons à l’analyse.</p>
<h2>Quelle analyse ?</h2>
<p>Il faut bien se dire que l’on compare des études avec des buts différents : mesurer l’efficacité des CDNs, des FAI ou avoir un vision plus globale oriente les méthodologies. C’est pour cela que j’explicite ma réfléxion, pour que vous utilisiez les commentaires pour critiquer ma tentative de compréhension.</p>
<h3>Le débit moyen</h3>
<p>Les statistiques d’abonnement de l’ARCEP montrent 2,7% d’abonnés fibres par rapport au nombre total d’abonnement, alors que Degrouptest reçoit 13% de ses mesures depuis la fibre. Nous pouvons donc requalifier le débit moyen de Degrouptest : <strong>5,01Mb/s</strong> (97,3 % * 5,4 Mb/s + 2,7 % * 27,3 Mb/s = 5,01 Mb/s).
Cette nouvelle moyenne se situe entre celle d’Akamai pour la France (4,6 Mb/s), et celle de 60 millions de consommateurs (5,6 Mb/s). Akamai ne détaille pas sa méthode mais on suppose qu’elle est plus représentative, car non volontaire. Elle ne mesure cependant que la connexion entre Akamai et un internaute et Cedexis nous indique qu’Akamai n’est pas le plus performant. Il faudrait donc légèrement remonter ce chiffre. D’un autre côté, 60 millions de consommateurs a fait son étude en 2011, et entre-temps DegroupTest semble indiquer une baisse de débit ADSL de 600 Ko/s. Il faudrait donc revoir leur moyenne à la baisse. Les chiffres convergent vers la valeur DegroupTest, que j’estime légèrement surestimée (population trop “aware”).</p>
<p>Pour arrondir, on peut donc <strong>situer un débit moyen à 4,8Mb/s</strong> sur ligne fixe. Concernant le débit mobile moyen, Akamai et Degrouptest ont à 30Kb/s près la même moyenne, on peut l’arrondir à <strong>fixer à 2,5Mb/s</strong>.</p>
<h3>La latence</h3>
<p>On applique la correction des stats ARCEP sur les stats DegroupTest et on obtient cette valeur arrondie : <strong>85ms</strong>. Cependant en regardant les données Cedexis, on voit des temps de réponse bien supérieurs, de 110ms par exemple pour récupérer un statique sur un CDN populaire comme Akamai. Cela est assez cohérent avec le fait qu’un CDN rajoute forcément un peu de latence (temps de routage supplémentaire, mise en cache, etc.) et que la population Degrouptest est surement un peu moins représentative que celle mesurée par Cedexis.</p>
<p>Concernant le ping sur mobile, on manque cruellement de données, la seule qu’on ait trouvé pour la France est celle de Degrouptest de 200ms. Dans des études bien moins larges dans d’autres pays, les chiffres varient de <a href="http://www.webperformancetoday.com/2012/04/02/mobile-versus-desktop-latency/">200ms</a> à <a href="http://www.webperformancetoday.com/2011/10/26/interesting-findings-3g-mobile-performance-is-up-to-10x-slower-than-throttled-broadband-service/">300ms</a>.</p>
<p>Pour la latence, nous nous fixons donc une moyenne arrondie de <strong>95ms sur ligne fixe</strong> et <strong>200ms sur mobile</strong>.</p>
<h3>Le cas du mobile</h3>
<p>Donc le débit moyen sur réseau mobile (2,5Mb/s) est plus élevé que 25% des ADSL de France. La latence est tout de même deux à trois fois plus élevée. Mais même en calibrant vos tests avec ces données et en supposant que vous les exécutiez depuis <a href="http://mobitest.akamai.com/m/index.cgi">un vrai mobile</a>, vous réalisez vite que quelque chose cloche : le ressenti utilisateur n’est pas du tout le même. En fait il manque une dernière composante, assez rare en ADSL : <strong>la perte de paquets TCP</strong>. Constamment les connexions ouvertes entre votre téléphone et l’antenne relai sont interrompues par des obstacles physiques, électro-magnétiques ou un changement d’antenne. Je n’ai pas trouvé d’étude avec un taux moyen de perte de paquet, aussi c’est un peu au pifomètre que j’utilise la valeur 25%. à débattre.
Il manque également le côté aléatoire de la connexion qui fait tout le charme des réseaux mobiles : parfois vous téléchargez une image en une demie seconde, parfois vous n’arrivez pas à avoir un octet pendant plusieurs minutes.
Exécutez régulièrement les applications <a href="http://speedtest.net/mobile.php">speedtest</a> ou Degroupest (<a href="http://links.fhsarl.com/pplipple">iOS</a>, <a href="http://links.fhsarl.com/applendroid">Android</a>) pour halluciner sur la variabilité des caractéristiques réseau.</p>
<p>[caption id=”attachment_1137” align=”alignnone” width=”300”]<a href="http://braincracking.org/wp-content/uploads/2012/10/test-débit-mobile.png"><img class="size-medium wp-image-1137 " title="test débit mobile" src="http://braincracking.org/wp-content/uploads/2012/10/test-débit-mobile-300x258.png" alt="" width="300" height="258" /></a> Test débit mobile[/caption]</p>
<p>Sur quelques tests ci-dessus, sur 2 tests effectués à moins d’une minute d’intervalle et sans bouger, vous pouvez voir une différence de latence de 1 à 4. Vous pouvez également voir des tests qui vont de 14Kb/s à 9Mb/s, sans compter ceux qui n’aboutissent pas.</p>
<p>Mais non seulement il n’existe pas d’étude sur les trous réseau (en supposant qu’une telle étude puisse être représentative), mais en plus <strong>nous ne voulons pas introduire d’aléa dans le cadre du monitoring</strong>, ou alors comment savoir si une alerte en est vraiment une ? Nous devons malheureusement choisir des mesures dégradées mais fixes.</p>
<h3><a name="resume"></a>En résumé</h3>
<table><caption>Les connexions moyennes françaises en 2012</caption>
<tbody>
<tr>
<th></th>
<th scope="col">Lignes Fixes</th>
<th scope="col">Réseau mobile</th>
</tr>
<tr>
<th scope="row">Débit descendant</th>
<td>4,8Mb/s</td>
<td>2,5Mb/s</td>
</tr>
<tr>
<th scope="row">Latence</th>
<td>95ms</td>
<td>200ms
(+25% de perte de paquets)</td>
</tr>
</tbody>
</table>
<h3>Il n'y a pas de connexion type</h3>
<p>Mon prof de stats me tirerait les oreilles si on s’arrêtait là. En effet une moyenne seule est à peu près inutile sans <a href="http://fr.wikipedia.org/wiki/%C3%89cart_type">l’écart-type</a> qui permet de juger de la représentativité de cette moyenne. En fait une moyenne est certes pratique à manipuler, et il est utile de surveiller son évolution, mais <strong>elle est particulièrement trompeuse</strong> si la répartition des mesures n’est pas concentrée autour de la moyenne.
Et justement les études d’Akamai, de l’ARCEP et de 60 millions de consommateurs montrent qu’il y a <strong>de fortes disparités dans l’équipement des utilisateurs</strong>.</p>
<ul>
<li>L'ARCEP nous dit qu'il reste 1,4% de gens en RTC (56Kb/s), soit à l'échelle de la France 300 000 personnes. Dur de savoir si c'est leur unique moyen d'accéder au Web, mais c'est un débit 500 fois inférieur aux mobiles ! Akamai signale également un faible taux de débit inférieurs à 256Kb/s</li>
<li>DegroupTest nous avertit sur le fait que depuis la France d'outre-mer, on a du 250ms de ping en moyenne, l'équivalent du mobile, et un débit 40% inférieur à la métropole</li>
<li>60 millions de consommateurs et Akamai montrent que <strong>50% des utilisateurs ont moins de 5Mb/s</strong>, avec une répartition qui a l'air à peu près égale par tranche de 1Mb/s : 10% ont moins de 1Mb/s, <strong>20% moins de 2Mb/s</strong>, etc.</li>
<li>les tuyaux du Web français sont embouteillés le soir, avec une <strong>perte de 20% de débit entre 21h et 22h</strong>.</li>
</ul>
<p>[caption id=”attachment_1144” align=”alignnone” width=”509”]<a href="http://braincracking.org/wp-content/uploads/2012/10/répartition-débits.png"><img class="size-full wp-image-1144" title="répartition débits" src="http://braincracking.org/wp-content/uploads/2012/10/répartition-débits.png" alt="" width="509" height="369" /></a> Il n’y a pas d’ADSL type en France[/caption]</p>
<p><strong>Nous nous sommes donc posés la mauvaise question</strong> : calibrer vos tests sur LA moyenne française est à peine moins trompeur que de mesurer depuis votre poste avec votre navigateur récent, sur votre réseau d’entreprise branché à la fibre (oui tout le monde le fait, et c’est MAL).</p>
<h2>Enfin : quelle stratégie de test ?</h2>
<p>Tous ces chiffres vont nous permettre de définir une vraie stratégie de test de la performance perçue. Cela commence par définir des objectifs de réactivité raisonnables en terme de confort d’utilisation, par exemple :</p>
<ul>
<li>afficher quelque chose avant 1 seconde (start render)</li>
<li>promesse / fonction principale de la page affichée avant 2 secondes</li>
<li>fonction principale interactive avant 5 secondes</li>
</ul>
<p>Note : les valeurs que j’indique ici sont un peu génériques, et sont à définir entre les équipes “produit” (marketing, ergonomie) et les équipes techniques (développeurs, sysadmins).
Vous devez maintenant être cruel et déterminer quelle proportion d’utilisateurs n’aura pas droit à cette rapidité. C’est un peu rude dit comme ça, mais une page rapide pour “tout le monde” coûte très cher. Généralement on vise à satisfaire par exemple 80 % de la population.</p>
<p>Voici un petit tableau qui devrait vous aider à prendre vos décisions :</p>
<table><caption>Calibrage des tests pour ligne fixe</caption>
<tbody>
<tr>
<th scope="col">Un débit de <strong>X</strong> Mb/s</th>
<th scope="col">et une latence de <strong>Y</strong> ms</th>
<th scope="col">laissent de côté <strong>Z</strong> % des visiteurs</th>
</tr>
<tr>
<td>5.0Mb/s</td>
<td>95ms</td>
<td>50%</td>
</tr>
<tr>
<td>4.0Mb/s</td>
<td>100ms</td>
<td>40%</td>
</tr>
<tr>
<td>3.0Mb/s</td>
<td>110ms</td>
<td>30%</td>
</tr>
<tr>
<td>2.0Mb/s</td>
<td>120ms</td>
<td>20%</td>
</tr>
</tbody>
</table>
<p>Remarques :</p>
<ul>
<li>Les débits et pourcentages semblent joliment arrondis. C'est de la chance.</li>
<li>Pour le ping, on n'a aucune stat sur la répartition réelle, ce sont donc des suppositions personnelles que je vous invite à critiquer</li>
</ul>
<p>Pour le réseau mobile, on est bien embêté car on n’a aucune idée de la répartition ! On sait juste qu’elle est très volatile, ne serait-ce que pour le même utilisateur. Donc, soit on joue ça au doigt mouillé avec la certitude de se tromper, soit on utilise la moyenne qui est pratiquement un mensonge. Mmmm. Choix cornélien mais, à choisir, autant partir sur une valeur un tout petit peu objective, c’est-à-dire la moyenne constatée, et y rajouter de la perte de paquets.</p>
<p>Je mettrais donc ces 3 valeurs par ordre de priorité :</p>
<ol>
<li>3,0Mb/s avec une latence de 110ms pour représenter 70% de vos utilisateurs</li>
<li>2,5Mb/s avec une latence de 200ms et <strong>une perte de paquets de 25%</strong> pour l'ensemble des utilisateurs de réseau mobile</li>
<li>4,80Mb/s avec une latence de 95ms pour "la moyenne" des connexion filaires</li>
</ol>
<p>La moyenne reste là surtout parce que c’est un chiffre que tout le monde (croit) comprendre et pour comparer mobile et bureau.</p>
<p>Si vous avez une population particulière (outre-mer, visiteurs à 21 h, habitant une grande ville, professionnelle, très mobile, etc.), il va vous falloir fouiller dans les chiffres ci-dessus pour trouver ce qui se rapproche le plus de vos utilisateurs. Ou, bien sûr, monter votre propre monitoring des capacités de vos utilisateurs.</p>
<h2>Résultats</h2>
<p>Pour conclure, comparons les valeurs DSL par défaut de WebPageTest.org avec celles que nous avons déterminées pour représenter 70 % des français. J’ai lancé un test sur un site moyennement complexe, qui n’a pas (encore) été optimisé et qui fait partie des sites à forte audience de France : Pluzz.fr.</p>
<p>[caption id=”attachment_1181” align=”alignnone” width=”1544”]<a href="http://braincracking.org/wp-content/uploads/2012/11/pluzz-comparaison-valeurs-defaut-et-custom.png"><img src="http://braincracking.org/wp-content/uploads/2012/11/pluzz-comparaison-valeurs-defaut-et-custom.png" alt="" title="pluzz-comparaison-valeurs-defaut-et-custom" width="1544" height="290" class="size-full wp-image-1181" /></a> Comparaison valeurs par défaut et personnalisées[/caption]</p>
<p>Pour plus de détails, voir <a href="http://www.webpagetest.org/video/compare.php?tests=121014_2994e714a56d3b251702fd9e2dcf43c0%2C121014_aa3557eec96550f644cc462b1d3647bb&thumbSize=200&ival=100&end=visual">le détail du déroulé</a>. Avec les valeurs par défaut, ce site s’affiche en moins d’une seconde, donnant l’impression que l’optimisation n’est pas nécessaire. Avec nos valeurs plus réalistes prenant en compte 70 % de la population, le start render se situe à 1,4 seconde, confirmant le besoin d’entamer des travaux si l’on veut atteindre les objectifs de performance perçue.</p>À quelle vitesse s’affiche un site ? La question est importante tant le confort d’utilisation et les revenus d’un site peuvent dépendre de cette réponse. Vous intégrez probablement déjà des pratiques visant à améliorer le rendu des pages et leur réactivité, et si vous êtes en phase d’industrialisation, vous faites peut-être du monitoring de la performance. Au moment de paramétrer votre solution, une question a dû vous poser problème : quelle est la connexion type en France ? Et pour les mobiles ?Performance web mobile, chargement de page 2/22012-11-06T15:04:21+00:002012-11-06T15:04:21+00:00/2012/11/06/performance-web-mobile-chargement-de-page-22<h1>Reprioriser les optimisations</h1>
<p>On l’a vu <a href="http://braincracking.org/?p=1156">dans le premier article</a>, certaines techniques pourtant reconnues peuvent devenir contre-productives si l’on ne fait pas les tests nécessaires. Ne jetons cependant pas tout aux orties, voici les techniques que vous devrez maîtriser plus que jamais et pousser dans leurs extrêmes si vous voulez obtenir un résultat correct sur mobile ; par ordre de priorité :</p>
<h2>1. Travailler le chemin critique</h2>
<p><strong>La notion de performance est plus un ressenti qu’une collection de chiffres</strong>. Lorsque vous devez retravailler une page pour un réseau mobile, demandez-vous ce que l’utilisateur est vraiment venu voir et dégagez le passage pour afficher au plus vite la zone ou la rendre fonctionnelle :</p>
<ul>
<li>pas (trop) de redirections, et surtout pas côté client ;</li>
<li>concaténation ou mise en ligne des CSS ;</li>
<li>un code HTML / CSS prêt à "marcher" sans JavaScript : pour un article, c’est facile, on affiche le texte (sans police de caractère, jamais) ; pour une vidéo, on peut utiliser HTML5 comme interface de lecture en attendant JavaScript. Si vous dépendez de JavaScript pour afficher la fonctionalité (un webmail par exemple), affichez au moins un indicateur de chargement ;</li>
<li>scripts asynchrones comme vu dans le premier article ;</li>
<li>JavaScript chargé et exécuté par ordre de priorité : on peut très bien imaginer de charger d’abord les fichiers JavaScript concaténés qui concernent la zone principale, puis un second pack de fichiers qui concernerait tout le reste de la page.</li>
</ul>
<h2>2. Minimiser le nombre de requêtes HTTP</h2>
<p>On l’a vu lors de l’exemple du <em>domain-sharding</em>, la première limite vient du réseau : le même mobile sur la même session de surf pourra passer d’un WiFi de bonne qualité à une absence complète de réseau. Vous ne pouvez jamais prévoir quand la latence va s’allonger jusqu’au <em>timeout</em> ou quand le débit passera en dessous de ce dont vous auriez besoin pour afficher une simple image. En supposant que vous serviez votre site classique sur mobile (1 Mo, 100 requêtes, 15 domaines… <a href="http://httparchive.org/trends.php">source http archive</a>), il va falloir faire baisser de manière drastique le nombre de requêtes pour minimiser les effets de latence ou de timeout et maximiser l’utilisation de la bande passante. Cet objectif est là pour durer et la plupart des techniques classiques utilisées jusqu’ici sont non seulement à investiguer, mais à pousser dans leurs extrêmes.</p>
<h3>La concaténation</h3>
<p>Il est plus que jamais temps de faire de vos CSS et JS des fichiers uniques ou presque. De base dans <a href="http://rubyonrails.org/">Ruby on Rails</a>, avec des plugins divers pour <a href="http://www.spip.net/">Spip</a> ou <a href="http://drupal.org/">Drupal</a>, avec <a href="https://github.com/widmogrod/zf2-assetic-module">Assetic</a> pour Zend 2, des dizaines de librairies pour d’autres projets PHP, avec <a href="http://sass-lang.com/">SASS</a> côté CSS et des outils comme <a href="http://yuilibrary.com/projects/yuicompressor/">YUI Compressor</a>.
Il n’y a plus vraiment d’excuses pour ne pas gérer correctement vos JS et CSS, sauf peut être le coût de refactoring initial, mais il reste généralement négligeable par rapport aux bénéfices attendus.</p>
<p>À noter que les stratégies de concaténation vont varier en fonction de la taille de votre site : si il est relativement petit, mettez tous vos CSS dans une seule feuille ; si il est plus grand, mettez le CSS commun dans une première feuille et les CSS de chaque type de page dans une autre. Ne dépassez jamais 2 feuilles par page. En JavaScript, les stratégies sont plus variées du moment que vous utilisez l’asynchrone : un fichier unique pour tout le site si il est petit, un fichier par zone fonctionnelle sinon (avec chargement asynchrone).</p>
<h3>Les images</h3>
<p>Les images sont généralement petites et nombreuses, c’est donc votre seconde cible après le CSS. Vous avez forcément déjà entendu parler du <a href="http://www.alsacreations.com/tuto/lire/1068-sprites-css-background-position.html">spriting</a> ; sur mobile, vous pouvez aller encore plus loin avec les caractères unicode : lorsque vous avez beaucoup d’icônes, essayez de voir si vous pouvez en réutiliser (♻) qui viendraient directement des polices du système !
Attention à la compatibilité navigateur ET système d’exploitation. Pour les mobiles, c’est généralement bon ; pour IE sur XP, vous n’aurez qu’un choix limité.</p>
<p>Encodage des images : visez le zéro requête en encodant vos images en texte directement dans le CSS, voire dans le HTML. C’est un peu fastidieux à faire à la main mais un simple script PHP suffit :</p>
<pre><code>print '<img src="data:image/png;base64,'.base64_encode(file_get_contents('/path/to/image.png')).'" />';</code></pre>
<p>Qui vous donnera en sortie quelque chose de ce genre :</p>
<pre><code>
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAABO1BMVEX92RlCQj+7vLzvzSzmhgnXgybyz3uLj5T13mj99cDwrmWMfzXmyTz78bT1zUf++uX437bxzqzmpj/dlDdcVUbuxnjszDLq6uvqmS/43Hv99t+foaOMgl5tYDjsu1vfwT388NijlXr52Vn22Y3999Z2bmD34svhrFlQTD378ajuwWXThzH668PpsFbvw5zrtU2Vh03x0Y3MzMz73kr664qZmZnmnUblqm/7w1HokRbefBb//e324HL2z0TsuENYRC399s/343z46paYijr88s/z1KXvyIvspkzroTj81yL56cnajjLwxm9KSkLmqj1gVj+Xg2H3vkv+/tz35XL+3CD89cj45Lf88MSipab13FnvtGP224T45Yb78rr++uv3zij25Grz1Ub1yi7wxFztu2vYiTH95kfqs2v45csXvxkaAAAAAXRSTlMAQObYZgAAAMlJREFUaN5jYAACY0llQzafZFsGCAiJDvNSkJJSEEjR9wQLROvw86s4OISqxLkJgfhKYfz8/NZA4MBrnW4HFGBzkZKKAwlIxWpmCAMFRKVCQ1VUXFzCY52CpVKBAlpeKo4xUVFR7jGRJmreQAE+N2sbCRsbDhuWxCBzP5Cpcq5BHjYJCTaWnIKsYHs9dV25bIDAXY81CeIyT3WzwLQ0CyvteKhTGcQCFJ25Ve2ZGeBAJiLClN0fwWcwYBI3kpZFEuAR8WXUkGfACgC55CKssDjPhQAAAABJRU5ErkJggg==" />
</code></pre>
<p>Des frameworks complets comme SASS permettent de le faire dans le CSS. Vous pouvez utiliser cette technique à partir de IE8, et envisager <a href="http://www.phpied.com/the-proper-mhtml-syntax/">l’équivalent MHTML</a> pour IE7.</p>
<h3>Le nombre de domaines</h3>
<p>Le site moyen a entre 10 et 15 domaines à faire résoudre et il n’y a pas eu d’études sur l’utilisation du cache DNS sur mobile. Pratiquement invisible sur ordinateur de bureau, le coût de recherche DNS est peut être dix fois plus important sur mobile à cause de la latence. Chaque recherche de domaine va bloquer une file de téléchargement en attendant la réponse. Donc, soit vous sacrifiez trackers, publicité, widgets et autres sources externes, soit vous les dépriorisez fortement, tout simplement en les appelant après vos propres fichiers.</p>
<h3>Connexions permanentes</h3>
<p>Si vous avez une version dédiée mobile de votre site (pas de problématique de référencement, code spécifique), autant y aller à fond et utiliser une technologie très prometteuse pour contrer les effets de la latence : WebSocket. Pointez deux navigateurs <a href="http://html5demos.com/web-socket">cette page de démo</a>, et observez le peu de délai dans la transmission d’une info d’un navigateur à l’autre, réseau mobile ou pas. Websocket ou d’autres techniques de connexions permanentes peuvent être détournées : vous chargez en une seule requête une page initiale ne contenant que le strict nécessaire de CSS, de contenu et de JS, puis vous chargez tout le reste (images, CSS, JS, contenu) via cette connexion, que vous pouvez même réutiliser pour renvoyer des informations (AJAX, tracking…), sans payer le prix d’ouverture de nouvelles connexions HTTP.
Je n’ai pas encore vu d’équipe web déclarer avoir implémenter cela, mais ça ne saurait tarder. Au pire, patientez un peu, je vais coder une démo sur de vrais sites.</p>
<h2>3. Le cache et l’offline</h2>
<p>Comme avant, il va falloir que vous commenciez par maîtriser absolument toutes les URLs vers les statiques de votre site pour pouvoir les servir avec des caches agressifs, sur des domaines sans cookies. Ça c’est surtout pour les navigateurs de bureau car <a href="http://www.guypo.com/mobile/understanding-mobile-cache-sizes/">le cache des navigateurs mobile est une vraie calamité</a> en terme d’efficacité ! Il y a beaucoup trop de limites (nombre d’objets, taille maximale par objet, taille maximale par site et taille maximale pour tout le navigateur) pour espérer de vrais gains avec les comportements par défaut. Attention il ne faut pas les ignorer pour autant, si votre visiteur va d’une page à l’autre, il sentira la différence.</p>
<p>Les sites très efficaces en sont donc à utiliser HTML5 localStorage pour stocker des JS, des CSS et même des images (encodées en base64). C’est la technique choisie par LinkedIn mobile, Bing ou GMail, et elle demande un certain niveau de maîtrise de son application. Par exemple, imaginez-vous sur une page d’accueil : vous allez télécharger via XHR tous les objets dont vous avez ou aurez besoin, puis les stocker dans localStorage.</p>
<pre><code>var loader = new XMLHttpRequest();
loader.open("GET", "http://example.com/js/main.js");
loader.onload = function(e) {
localStorage.setItem('main.js', e.response);
}
loader.send();</code></pre>
<p>Ensuite, au moment ou vous en avez besoin, vous vérifiez la présence des fichiers voulus dans votre cache maison.</p>
<pre><code>if( localStorage.getItem('main.js') !== null ) {
eval( localStorage.getItem('main.js') );
}</code></pre>
<p>Oui, vous faites à la main le boulot du navigateur (télécharger, gérer le cache, insérer…) mais c’est avec ce genre de code un peu extrême que l’expérience utilisateur sur mobile fera la différence. En prime, les navigateurs de bureau en profiteront aussi.</p>
<p>Une note pour ceux qui ont entendu dire que LocalStorage était contre-performant car synchrone (lorsque vous écrivez, vous bloquez le programme le temps que le disque ait fini de travailler) :</p>
<ul>
<li>c’est évitable facilement en passant par un wrapper qui va simplement effectuer l’écriture en asynchrone ;</li>
<li>les mobiles écrivant tout en RAM / Flash disk, les temps d’accès sont de toute façon négligeables.</li>
</ul>
<p>Vous pouvez encore passer au niveau supérieur en utilisant une technique qui a autant fait ses preuves que montré ses limites : l’utilisation de la <a href="http://appcachefacts.info/">spécification Application Cache</a>. <strong>Native, bien supportée par tous les navigateurs modernes mobiles ou de bureau</strong>, elle permet de déclarer en un fichier (le manifeste) quels sont les fichiers à télécharger, quelles ressources doivent continuer à être servies en ligne, et quoi afficher lorsque la connexion n’est plus là. Au contraire de la gestion du cache présentée précédemment qui est personnalisée et donc très adaptée à votre application, Application Cache vous force à coder d’une certaine manière (maîtriser absolument toutes ses URLs), présente certains manques (invalider le cache de certains fichiers seulement) et il faut un petit temps d’adaptation pendant la phase de développement pour comprendre son fonctionnement intrinsèque. En revanche, en terme de performance et de mode de distribution, c’est parfait !</p>
<h2>4. Réduire la taille</h2>
<h3>Minification et compression des JS / CSS</h3>
<p>La minification est généralement exécutée au déploiement du site juste après la concaténation et vous pouvez généralement gagner 50 % du poids des fichiers avec des outils solides comme <a href="http://yuilibrary.com/projects/yuicompressor/">YUI Compressor</a>. Activez la compression gzip par-dessus et vous aurez gagné au final 90 % du poids initial de vos fichiers. Sachant qu’il n’est plus rare de trouver des sites avec 200-500 Ko de CSS et de JS, le gain est assez important.</p>
<h3>Les photos</h3>
<p>Pour les images de l’interface, comme pour les photos, vous avez beaucoup à gagner en compressant au maximum les images servies. Encore plus efficace : ne les afficher que si elles sont nécessaires. Dans le cas d’une application Web Mobile ou l’interface permet de beaucoup scroller, considérez comme une amélioration majeure de faire du lazy-loading sur les images, c’est à dire de ne télécharger l’image que lorsqu’elle est sur le point d’être affichée.
En terme de compresseur d’image sans perte de qualité, voyez avec <a href="http://advsys.net/ken/utils.htm">PNGout</a> (ligne de commande) et <a href="http://pnggauntlet.com/">PNG Gauntlet</a> (son interface visuelle) pour compresser efficacement les PNG, ou <a href="http://sauron.jyu.fi/cgi-bin/viewcvs.cgi/jpegoptim/">JPGoptim</a> (ligne de commande) et <a href="http://imageoptim.com/">Image Optim</a> (meta interface pour Mac) pour les JPG.</p>
<h3>Les cookies</h3>
<p>Là aussi, investissez du temps pour maîtriser toutes les URLs de toutes les ressources statiques de votre site , afin de les mettre sur des domaines séparés du domaine principal. Imaginez un cookie d’un peu moins d’un Ko qui serait envoyé à la centaine d’objets généralement appelée par une page Web classique : vous demandez à votre utilisateur d’uploader 100 Ko de données inutiles. Une autre piste pour remplacer les cookies qui sont parfois servis sur des pages statiques qui n’utilisent même pas de données personnalisées est de ne pas les utiliser du tout… pour les remplacer par HTML5 localStorage si vous avez tout de même besoin de persistance côté client (préférence d’affichage, historique…). Pour la compatibilité navigateur, passez par des petites librairies spécialisées.</p>
<h1>En résumé</h1>
<p>Vous voilà donc paré pour arrêter de perdre vos clients dès qu’ils essayent d’accéder à votre site à partir d’un réseau mobile. Nous avons vu que :</p>
<ul>
<li>la performance est empirique : testez même les grands classiques avant de choisir vos solutions ;</li>
<li>la plupart des techniques déjà connues marchent mais sont à traiter dans un autre ordre ;</li>
<li>ces techniques sont à pousser dans leurs extrêmes ;</li>
<li>de nouvelles possibilités sont apparues.</li>
</ul>
<p>Et ne comptez pas sur l’évolution future de l’équipement réseau : sauf inversion de la tendance actuelle, non seulement vos sites vont continuer à grossir mais en plus les futurs utilisateurs des réseaux 4G voire Wimax auront toujours des doigts, des murs, des gens et de l’atmosphère à traverser avant d’arriver sur vos serveurs.</p>
<p>L’optimisation, c’est maintenant.</p>Reprioriser les optimisations On l’a vu dans le premier article, certaines techniques pourtant reconnues peuvent devenir contre-productives si l’on ne fait pas les tests nécessaires. Ne jetons cependant pas tout aux orties, voici les techniques que vous devrez maîtriser plus que jamais et pousser dans leurs extrêmes si vous voulez obtenir un résultat correct sur mobile ; par ordre de priorité :Performance web mobile, chargement de page 1/22012-11-06T15:04:21+00:002012-11-06T15:04:21+00:00/2012/11/06/performance-web-mobile-chargement-de-page-12<p><em>Cet article a été initialement publié sur <a title="le train de 13h37" href="http://letrainde13h37.fr/14/performance-web-mobile-chargement-de-page-1-sur-2/">le train de 13h37</a>, magazine du développement web de grande qualité, qui a le double mérite de <a href="http://letrainde13h37.fr/rejoignez-nous/">rémunérer</a> ses auteurs et de proposer les articles avec une licence <a href="http://creativecommons.org/licenses/by-nc-sa/2.0/fr/">CC BY-NC-SA</a>.</em></p>
<h1>Introduction</h1>
<p>Vos utilisateurs viennent chez vous sur mobile : <a href="http://gs.statcounter.com/#mobile_vs_desktop-FR-monthly-201208-201210">5 % selon StatsCounter</a> ; <a href="http://www.atinternet.fr/documents/systemes-dexploitation-pres-de-7-des-visites-europeennes-pour-ios/">10 % selon ATInternet</a> ; 9 % d’après ce que je vois chez mes clients dont le site n’a PAS été pensé pour du mobile. Évidemment cette part est en augmentation constante, a déjà largement dépassé IE7, va dépasser IE8, et il est raisonnable de penser que, ces statistiques étant récoltées via JavaScript, certaines tentatives d’accès ne soient même pas comptabilisées ! Que vous le vouliez ou non, que votre site ait été fait ou pas pour cet usage, <strong>vos utilisateurs essaient de venir sur votre site dans des conditions de réseau qui ne vous arrangent pas forcément</strong>, mais qui vont vous poser quelques défis intéressants.</p>
<h1>De quels mobiles parle-t-on ?</h1>
<p>Plus on rentre dans les détails des navigateurs, des systèmes d’exploitation et des capacités matérielles, et plus il semble illusoire de définir ce qu’est vraiment un mobile. Restons pragmatiques et, dans le cadre de cet article sur la performance, précisons ce que nous allons considérer comme notre cible :</p>
<ul>
<li>capacité du navigateur à afficher des sites non spécifiquement prévus pour mobiles ;</li>
<li>navigation "tactile" ;</li>
<li>matériel sous-dimensionné par rapport à la machine de bureau moyenne ;</li>
<li>qualité de réseau fluctuante.</li>
</ul>
<p>Ce dernier critère peut prêter à discussion, tant on observe d’utilisateurs mobiles accéder aux sites depuis le confort du WiFi de la maison : par exemple, la moitié des visiteurs dits “mobile” sont en fait sur iPad, ce qui implique (le plus souvent) le WiFi.</p>
<p>Mais dans le cadre de cet article nous allons surtout nous intéresser aux “problèmes supplémentaires à résoudre pour le chargement des pages” : les temps de chargement depuis une tablette / mobile sur WiFi sont largement comparables aux temps de chargement sur certains navigateurs de bureau.</p>
<h1>Comment tester ?</h1>
<p>Tout développement commence par la mise dans de bonnes conditions pour tester ; la mesure a lieu ensuite dans un chantier performance web. Le banc de test idéal pour le mobile est simplement impossible à monter et force est de constater que les éditeurs de sites, de l’indépendant aux sites avec des millions de pages vues, se contentent généralement de l’iPhone de l’intégrateur et éventuellement de l’Android-qui-s’appelle-revient du collègue.</p>
<p>Si l’on regarde les parts de marché en France, cela correspond effectivement à une réalité : nous avons Safari pour iOS partagé entre les différents iPhone et iPad, et le navigateur par défaut d’Android distribué avec les version 2.x. En queue de peloton et souvent ignorés, à tort pour les valeurs d’interopérabilité du Web, viennent les Opera (mini ou mobile) pourtant ultradominants dans d’autres pays, Firefox mobile, ainsi que différentes variantes de Webkit sur les OS mobiles survivants (Blackberry, SymbianOS, LG…), ainsi que IE Windows.</p>
<p>Ah, et ne comptez pas sur les émulateurs : ils sont déjà à peine acceptables pour tester les fonctionnalités, alors question performance ils sont tout simplement disqualifiés d’office ! Entre l’utilisation du processeur de la machine hôte, une connexion filaire sans défaut et les approximations inhérentes à tout émulateur, vous n’aurez jamais quoi que ce soit de réaliste dans votre quête de la fluidité.</p>
<p>La solution minimale du pauvre, c’est de conserver ses mobiles et d’attendre qu’ils vieillissent ou d’acheter des occasions datant d’un an ou deux, sans mettre à jour le système d’exploitation. Pour simuler un réseau 3G, on pourra utiliser des proxys via WiFi qui dégradent volontairement la connexion. Il y a une grosse dizaine de solutions, généralement limitées à un seul OS (souvent Mac). La plus compatible et reconnue est le proxy payant <a href="http://www.charlesproxy.com/">Charles</a> mais la gratuité de <a href="http://www.jopa.fr/index.php/2010/07/17/limiter-la-bande-passante-avec-trickle/">Trickle</a> (Linux), <a href="http://osxdaily.com/2011/08/10/simulate-internet-connectivity-bandwidth-speeds-network-link-conditioner/">Network Link Conditioner</a> (OSX 10.6) ou <a href="http://slowyapp.com/">Slowy App</a> (OSX 10.5) peut vous séduire.</p>
<p>Le principe est simple : ces logiciels contiennent de quoi dégrader artificiellement une connexion (débit, latence, perte de paquets). Vous configurez votre téléphone pour utiliser votre machine comme proxy via WiFi, et voilà ! En prime, vous pouvez analyser les trames réseau qui passent. C’est moins cher que de vraies cartes SIM et la constance de la dégradation permet de faire des tests répétés, là où la qualité d’un vrai réseau data peut varier d’une minute à l’autre.</p>
<p>Pour être honnête, les techniques décrites dans cet article ont surtout été testées sur le navigateur Android 2.3 et Safari iOS 6, avec des vérifications fonctionnelles sur Firefox et Opera mobile sur Android, les deux absents notoires étant le Safari de Blackberry et Internet Explorer.</p>
<p>Signalons également que nous ignorons délibérément un bon milliard d’habitants sur cette planète qui se connectent au Net avec des mobiles pré-smartphones : techniquement difficiles à adresser sérieusement, ils sont également une minorité en France à posséder ces anciens mobiles et avoir souscrit un abonnement 3G.</p>
<h1>Chargez !</h1>
<h2>L’importance du réseau</h2>
<p>Croyez-le ou non, <strong>les débits théoriques en 3G et technologies au-delà peuvent être plus rapides que les débits constatés des ADSL</strong>. Mais comme finalement assez peu de gens font du Web depuis les laboratoires de tests des constructeurs, il va falloir composer avec la réalité physique : une couverture nuageuse un peu forte, un déplacement un peu rapide, <a href="http://www.clubic.com/smartphone/iphone/actualite-349254-iphone-4-reponse-steve-jobs-probleme-reception.html">un doigt mal placé</a>, un mur un peu épais, ou des gens autour de vous également équipés de mobiles (incroyable en 2012), et vous vous retrouvez rapidement avec des débits ridicules, des pertes de paquets énormes et le sentiment global qu’on vous a un peu survendu la “meilleure couverture réseau de France”.</p>
<p>Lorsque le réseau marche, l’utilisateur français moyen a <strong>en moyenne 2,50 Mb/s</strong> (<a href="http://letrainde13h37.fr/23/debit-latence-france-bien-calibrer-tests-performance/">sources</a>) ce qui est plus rapide que 25% des ADSL de France. Par contre, la latence ou ping est en moyenne à 200 ms soit le double de la moyenne en ADSL.</p>
<p>Depuis <a href="http://www.belshe.com/2010/05/24/more-bandwidth-doesnt-matter-much/">toujours</a>, la latence est l’ennemi numéro 1 de nos pages Web. Ceci est du à <a href="http://www.slideshare.net/edaspet/soire-webperf-du-29-nov-2010-latence-et-cdn">la nature même de HTTP</a> et signifie que pour un débit moyen seulement deux fois inférieur, le temps de chargement sera en fait 3 à 6 fois plus long sur mobile ! Pire, la perte de paquets est assez conséquente et inhérente à la technologie radio utilisée pour communiquer entre le mobile et l’antenne de l’opérateur. Et cette perte de paquets arrive plus fréquemment pour les petites requêtes… Exactement la manière dont sont composées la majorité de nos pages Web actuelles !</p>
<p>Ajoutez à cela qu’accrocher et maintenir le réseau data est une des opérations les plus coûteuses en terme de batterie : passez en mode avion sur votre smartphone, utilisez-le presque comme d’habitude (lectures, jeux…) et vous verrez qu’il peut tenir une seconde journée sans recharge. Et souvenez-vous des anciens portables sans data qui tenaient une semaine…</p>
<p>Bref, c’en est au point que les constructeurs optimisent comme ils peuvent le matériel, par exemple en mettant en veille la radio après un certain temps d’inutilisation. On se retrouve donc dans des situations où, le temps de lire cet article, l’antenne s’est déconnectée et il vous faudra plusieurs secondes pour que la requête suivante aboutisse !</p>
<p>Malgré cela, les utilisateurs ne sont pas beaucoup plus patients que sur un ordinateur de bureau : après 5 à 10 secondes de page blanche, une majorité d’utilisateurs a déjà rebroussé chemin, et autant vous dire qu’ils n’apparaissent même pas sur les statistiques collectées en JavaScript. Comment faire pour vaincre l’hémorragie de visiteurs et proposer des services de meilleure qualité perçue ?</p>
<p>Eh bien il va nous falloir revisiter tout l’arsenal des classiques de la Performance Web.</p>
<h2>Remettre en cause les classiques</h2>
<p>Lorsque le pape de la performance, Steve Souders, a émis chez Yahoo! <a href="http://developer.yahoo.com/performance/rules.html">les premières recommandations de performance Web</a>, c’était en faisant du reverse-engineering sur les moteurs IE6-IE7 et Firefox 2. C’était en 2005 et, avec l’abandon progressif du support de ces navigateurs antédiluviens (à l’échelle du Web) d’un côté et la banalisation de l’ADSL de l’autre, certaines règles ou astuces sont devenues moins importantes.</p>
<p>L’apparition de la navigation sur mobile en a même rendu certaines dangereuses !</p>
<h3>Le domain-sharding</h3>
<p>Citons par exemple le domain-sharding, qui consiste à répartir ses fichiers statiques sur plusieurs sous-domaines différents. Étonnamment populaire, à l’origine cette technique permettait de passer outre la limitation à 2 files de téléchargement par domaine et de bénéficier de toute la largeur des bandes passantes modernes. Mais à partir de IE8, sorti en 2009, cette technique devient inutile car le nombre de téléchargements simultanés passe de 2 à généralement 6 : les navigateurs se sont adaptés à la banalisation des gros débits et tentent d’en tirer parti.</p>
<p>Utilisons le service <a href="http://stevesouders.com/cuzillion/">Cuzillon</a> qui va nous permettre de <a href="http://stevesouders.com/cuzillion/?c0=bi1hfff0_0_f&c1=bi1hfff0_0_f&c2=bi1hfff0_0_f&c3=bi1hfff0_0_f&c4=bi1hfff0_0_f&c5=bi1hfff0_0_f&c6=bi2hfff0_0_f&c7=bi2hfff0_0_f&c8=bi2hfff0_0_f&c9=bi2hfff0_0_f&c10=bi2hfff0_0_f&c11=bi2hfff0_0_f&c12=bi3hfff0_0_f&c13=bi3hfff0_0_f&c14=bi3hfff0_0_f&c15=bi3hfff0_0_f&c16=bi3hfff0_0_f&c17=bi3hfff0_0_f&c18=bi4hfff0_0_f&c19=bi4hfff0_0_f&c20=bi4hfff0_0_f&c21=bi4hfff0_0_f&c22=bi4hfff0_0_f&c23=bi4hfff0_0_f&t=1344287686190">simuler une page avec 24 images réparties sur 4 domaines</a> et <a href="http://stevesouders.com/cuzillion/?c0=bi1hfff0_0_f&c1=bi1hfff0_0_f&c2=bi1hfff0_0_f&c3=bi1hfff0_0_f&c4=bi1hfff0_0_f&c5=bi1hfff0_0_f&c6=bi1hfff0_0_f&c7=bi1hfff0_0_f&c8=bi1hfff0_0_f&c9=bi1hfff0_0_f&c10=bi1hfff0_0_f&c11=bi1hfff0_0_f&c12=bi1hfff0_0_f&c13=bi1hfff0_0_f&c14=bi1hfff0_0_f&c15=bi1hfff0_0_f&c16=bi1hfff0_0_f&c17=bi1hfff0_0_f&c18=bi1hfff0_0_f&c19=bi1hfff0_0_f&c20=bi1hfff0_0_f&c21=bi1hfff0_0_f&c22=bi1hfff0_0_f&c23=bi1hfff0_0_f&t=1344289103427">une autre page avec 24 images sans domain-sharding</a>. Le service <a href="http://www.webpagetest.org/">WebPageTest</a> nous permet de voir qu’il y avait effectivement une amélioration sur le temps de chargement sur IE7 :</p>
<p>[caption id=”attachment_1158” align=”alignnone” width=”1251”]<a href="http://braincracking.org/wp-content/uploads/2012/11/ie7-sharding.png"><img class="size-full wp-image-1158" title="ie7-sharding" src="http://braincracking.org/wp-content/uploads/2012/11/ie7-sharding.png" alt="" width="1251" height="286" /></a> WebPagetest – Test de chargement d’une page sous IE7 avec (en bas) et sans sharding (en haut). <a href="http://www.webpagetest.org/video/compare.php?tests=120806_f7a9cc3a9bae3fff71e1ac524842bca3%2C120806_379be6337c90ce0cb2273ad4d88a709e&thumbSize=200&ival=500&end=all">Voir la source</a>[/caption]</p>
<p>Le test sur les mêmes URLs sur IE8 nous montre que l’on ne gagne plus grand chose en premier rendu (les petites images chargées) :</p>
<p>[caption id=”attachment_1159” align=”alignnone” width=”1058”]<a href="http://braincracking.org/wp-content/uploads/2012/11/ie8-sharding.png"><img class="size-full wp-image-1159" title="ie8-sharding" src="http://braincracking.org/wp-content/uploads/2012/11/ie8-sharding.png" alt="" width="1058" height="278" /></a> WebPagetest – Test de chargement d’une page sous IE8 avec (en bas) et sans sharding (en haut). <a href="http://www.webpagetest.org/video/compare.php?tests=120806_c2e00161edfe350d8d77969b44361ccc,120806_a0162c681568ff998a5cb40f6c03b8cd">Voir la source</a>[/caption]</p>
<p>On ne gagne plus rien car la parallélisation des téléchargements est assurée de base par le navigateur. On a en fait rajouté deux mini-problèmes en rajoutant un temps de résolution DNS supplémentaire par domaine, et en faisant concourrir plusieurs images pour la même bande passante. Je dis “mini-problème” en supposant qu’on ne ciblait que les navigateurs de bureau (c’est mal) où ces problèmes sur des vrais sites (75-150 requêtes, 10-15 domaines) passent finalement inaperçus, mais vous ciblez les mobiles, pas vrai ?</p>
<p>Sur mobile, les navigateurs ouvrent également plusieurs connexions par domaine et font même du multiplexing HTTP (plusieurs requêtes HTTP dans le même tuyau TCP) pour minimiser les problèmes liés à la latence. Vous prenez alors le risque de couper court à ces optimisations. Résultat ?</p>
<p>[caption id=”attachment_1160” align=”alignnone” width=”807”]<a href="http://braincracking.org/wp-content/uploads/2012/11/ios-sharding.png"><img class="size-full wp-image-1160" title="ios-sharding" src="http://braincracking.org/wp-content/uploads/2012/11/ios-sharding.png" alt="" width="807" height="495" /></a> WebPagetest – Test de chargement d’une page sous iOS avec (en bas) et sans sharding (en haut). <a href="http://www.webpagetest.org/video/compare.php?tests=120806_K6_W44,120806_EZ_W36">Voir la source</a>[/caption]</p>
<p><strong>L’affichage a été ralenti</strong> ! En forçant encore plus de domaines, vous demandez à plus d’objets de se partager la même bande passante, que vous devez considérer a priori comme rare sur mobile. Donc vous ralentissez l’affichage des premières images alors même que les suivantes ne sont pas immédiatement utiles. Ici, le temps de chargement total est en plus augmenté car le multiplexing n’a pas pu être utilisé, ce qui peut être le cas sur des sites avec un ratio ressources / domaine faible.</p>
<h3>Les scripts en bas de page</h3>
<p>Le temps avant le premier rendu (l’affichage des premiers pixels du site) est un peu le but ultime de la performance Web, car il y a une très forte corrélation entre ce temps et une augmentation des taux de transformation ( + rapide = + d’argent ).</p>
<p>Dans ce cadre, une autre technique populaire consiste à déplacer toutes les balises script en bas de page. Cette technique marche effectivement bien sur les anciens navigateurs et continue à faire ses preuves dans une moindre mesure sur les nouveaux malgré leurs progrès dans la prioritisation des téléchargements.</p>
<p>Regardons <a href="http://www.webpagetest.org/video/compare.php?tests=120806_K3_WTX,120806_PB_WT9">l’effet attendu avec IE8</a> sur une page simple où le téléchargement du JavaScript est retardé côté serveur.</p>
<p>[caption id=”attachment_1161” align=”alignnone” width=”2076”]<a href="http://braincracking.org/wp-content/uploads/2012/11/desktop-scriptsbottom.png"><img class="size-full wp-image-1161" title="desktop-scriptsbottom" src="http://braincracking.org/wp-content/uploads/2012/11/desktop-scriptsbottom.png" alt="" width="2076" height="298" /></a> Bureau : tests de comparaison de chargement d’une page avec les scripts en bas et en haut de page. <a href="http://www.webpagetest.org/video/compare.php?tests=120806_K3_WTX,120806_PB_WT9">voir la source</a>[/caption]</p>
<p>En haut, la page améliorée par déplacement des balises script ; on voit que le contenu (une simple phrase) s’affiche rapidement, alors qu’en bas la page blanche de la mort est de rigueur.</p>
<p>Passons sur mobile, si vous le voulez bien, et <a href="http://www.webpagetest.org/video/compare.php?tests=120806_A9_WQE,120806_BA_WQ9">testons Safari iOS et Android Browser 2.3.</a></p>
<p>[caption id=”attachment_1162” align=”alignnone” width=”1794”]<a href="http://braincracking.org/wp-content/uploads/2012/11/mobiles-scriptsbottom.png"><img class="size-full wp-image-1162" title="mobiles-scriptsbottom" src="http://braincracking.org/wp-content/uploads/2012/11/mobiles-scriptsbottom.png" alt="" width="1794" height="509" /></a> Mobiles Android et iOS : tests de comparaison de chargement d’une page avec les scripts en bas de page. <a href="http://www.webpagetest.org/video/compare.php?tests=120806_A9_WQE,120806_BA_WQ9">Voir la source</a>[/caption]</p>
<p>Android se comporte comme nos navigateurs de bureau et affiche le contenu dès qu’il l’a. Safari iOS préfère attendre le script avant d’afficher quoi que ce soit, sans doute par peur du FOUC (<em>Flash Of Unscripted Content</em>).</p>
<p>On peut débattre sur le fait qu’ergonomiquement il vaut mieux attendre une page bien faite (complètement scriptée) plutôt que d’afficher rapidement du contenu pas finalisé, mais <strong>les taux de tranformation montrent que l’affichage rapide rapporte plus</strong>.
Et de toute façon, en tant qu’auteur de la page, c’est à vous de juger si elle peut être vue momentanément sans scripting ou pas : si vous utilisez des méthodes de développement par couche (HTML, puis CSS, puis JS), ça devrait être bon. Si vous voulez de la performance, il va donc falloir faire évoluer votre technique d’inclusion de script pour passer à une version asynchrone.</p>
<p>Le chargement asynchrone à la base est bête comme chou :</p>
<pre><code>var oNode = document.createElement('script');
oNode.type = 'text/javascript';
oNode.src = 'http://domaine/monscript.js';
document.getElementsByTagName('head')[0].appendChild(oNode); </code></pre>
<p>Vous utilisez le DOM pour créer dynamiquement de nouveaux noeuds script qui vont charger des fichiers JS. Cette technique est notamment utilisée par les Google Analytics, boutons G+ et autres Like Facebook pour s’insérer dans vos sites sans introduire de dépendance forte vis-à-vis de leurs serveurs.
Autrement dit, lorsque leurs serveurs sont en rade ou bloqués par un proxy, le navigateur va tout de même afficher votre page (trop aimable). Utiliser cela avec ses propres scripts est globalement une bonne idée, et <strong>devient vital sur mobile</strong>. Vous aurez probablement besoin d’aller plus loin avec le chargement dynamique de scripts (exécuter du code lorsque le fichier est arrivé, gestion des dépendances et de l’ordre de chargement, concaténation de fichiers à la volée…), aussi pensez à utiliser des frameworks solides comme <a href="http://requirejs.org/">requireJS</a>, <a href="http://yepnopejs.com/">YepNope.js</a> (inclus avec Modernizr), <a href="http://headjs.com/">HeadJS</a>, <a href="http://labjs.com/">LabJS</a>… Chacun a sa philosophie.
Et puisque je vous tiens, voici le mien : <a href="https://github.com/jpvincent/LazyLoadLight">LazyLoadLight</a>, qui se veut léger et facile :)</p>
<p>Voir <a href="http://braincracking.org/?p=1164">la suite de cet article</a> qui parle des chemins à explorer pour des pages toujours plus rapides sur mobile.</p>Cet article a été initialement publié sur le train de 13h37, magazine du développement web de grande qualité, qui a le double mérite de rémunérer ses auteurs et de proposer les articles avec une licence CC BY-NC-SA. Introduction Vos utilisateurs viennent chez vous sur mobile : 5 % selon StatsCounter ; 10 % selon ATInternet ; 9 % d’après ce que je vois chez mes clients dont le site n’a PAS été pensé pour du mobile. Évidemment cette part est en augmentation constante, a déjà largement dépassé IE7, va dépasser IE8, et il est raisonnable de penser que, ces statistiques étant récoltées via JavaScript, certaines tentatives d’accès ne soient même pas comptabilisées ! Que vous le vouliez ou non, que votre site ait été fait ou pas pour cet usage, vos utilisateurs essaient de venir sur votre site dans des conditions de réseau qui ne vous arrangent pas forcément, mais qui vont vous poser quelques défis intéressants. De quels mobiles parle-t-on ? Plus on rentre dans les détails des navigateurs, des systèmes d’exploitation et des capacités matérielles, et plus il semble illusoire de définir ce qu’est vraiment un mobile. Restons pragmatiques et, dans le cadre de cet article sur la performance, précisons ce que nous allons considérer comme notre cible : capacité du navigateur à afficher des sites non spécifiquement prévus pour mobiles ; navigation "tactile" ; matériel sous-dimensionné par rapport à la machine de bureau moyenne ; qualité de réseau fluctuante. Ce dernier critère peut prêter à discussion, tant on observe d’utilisateurs mobiles accéder aux sites depuis le confort du WiFi de la maison : par exemple, la moitié des visiteurs dits “mobile” sont en fait sur iPad, ce qui implique (le plus souvent) le WiFi.HTML5, aujourd’hui2012-02-01T15:04:21+00:002012-02-01T15:04:21+00:00/2012/02/01/html5-aujourdhui<p>L’article suivant est la retranscription de l’article que j’ai écrit pour <a href="http://phpsolmag.org/">le magazine PHP Solutions</a>, dans le numéro hors série de 2011 consacré à HTML5. Le monde compliqué du droit d’auteur veut que ce texte ne m’appartienne pas, c’est donc avec leur permission express que j’en fait bénéficier mes lecteurs (oui toi). Toute reproduction est donc interdite.</p>
<p>Cet article se veut une introduction générale à HTML5, notamment pour les développeurs PHP qui sont généralement les mieux placés pour prendre au sérieux ce qu’il se passe au frontend. Si vous avez lu <a href="http://livre-html5.com/">mon livre sur HTML5</a> ou que vous suivez déjà régulièrement l’actualité, je doute que vous y appreniez grand chose. Il explique :</p>
<ul>
<li>l'origine de la spécification</li>
<li>comment les sites d'aujourd'hui peuvent en bénéficier</li>
<li>le futur des applications Web</li>
<li>les rares synergies entre HTML5 et PHP</li>
</ul>
<h2>Historique</h2>
<h3>Une définition variable</h3>
<p>Le terme même « HTML5 » couvre plusieurs définitions, et nous allons voir que le W3C a surpris tout le monde dans son choix final. Avant que le terme HTML5 n’apparaisse, HTML 4.01 était stabilisé et le W3C planchait sur XHTML 2 car pour eux la syntaxe stricte de XML allait permettre à tous les navigateurs d’être enfin d’accord sur les règles de <em>parsing</em>des pages.
<!--more-->
[caption id=”attachment_924” align=”aligncenter” width=”101” caption=”Le logo volontairement énigmatique du WHATWG”]<img class="size-full wp-image-924" title="logo_whatwg" src="http://94.23.51.58/jpvincent/blog/wp-content/uploads/2012/01/html5_intro_figure_01.png" alt="" width="101" height="101" />[/caption]</p>
<p>Nous sommes en 2004 et des représentants d’Apple, Opéra et Mozilla, pensant que XHTML 2 est une fausse bonne idée, décident de former un groupe dissident, dénommé non sans ironie WHATWG (What Working Group?). Ils travaillent de leur côté à leur vision du Web : celle d’une plateforme de développement universelle qui pourrait battre les applications natives, aux langages généralement propriétaires et fermés, en étant exécutable sur tous les systèmes et toutes les machines. Leur première spécification est « WebForms 2 », une évolution majeur des formulaires HTML que nous allons aborder plus loin. Vont venir s’ajouter beaucoup de petites spécifications regroupées sous le terme « Web Application 1 ».</p>
<p>En 2006 le terme HTML5 apparaît enfin : c’est la version approuvée par le W3C des spécifications travaillées par le WHATWG. Le Web a eu peur un instant que le W3C soit tellement discrédité par cette affaire que ses spécifications, déjà mal comprises des développeurs Web par la faute des implémentations non conformes de certains navigateurs, ne fassent plus autorité. Désormais cette réconciliation donne un nouveau rythme de travail : les navigateurs (hors Microsoft) testent des implémentations de fonctionnalité à toute vitesse, s’accordent sur les APIs finales et les formalisent, pendant que le W3C garde son rôle de gardien des clés et continue son travail de proposition de spécifications.</p>
<p>En 2009, le W3C accuse le coup du rejet par la communauté des développeurs Web de XHTML 2 et l’abandonne : l’inconvénient majeur de la syntaxe XML était qu’elle ne serait jamais rétrocompatible avec les navigateurs déjà en circulation. A cette époque déjà on a du mal à savoir ce que désigne vraiment HTML5 : des spécifications d’API sont réparties entre trois spécifications (« Web Application », « HTML5 » par le WHATWG et « HTML5 » par le W3C), et certaines devenues trop grosses ou pratiquement finalisées sont carrément sorties de HTML5 pour vivre par elle même.</p>
<p>[caption id=”attachment_925” align=”aligncenter” width=”256” caption=”Le logo officiel de HTML5”]<img class="size-full wp-image-925" title="logo_html5" src="http://94.23.51.58/jpvincent/blog/wp-content/uploads/2012/01/html5_intro_figure_02.png" alt="" width="256" height="256" />[/caption]</p>
<p>En 2011 le terme HTML5 est clairement un terme générique : le W3C déclare officiellement qu’il désigne désormais plus une idée de l’évolution du Web qu’une spécification en particulier, et lui donne par la même occasion un <strong>logo</strong>. Le WHATWG de soncôté bannit ce terme pour renommer ses spécifications « living standard », ce que l’on pourrait traduire par : « ne cherchez pas la stabilité chez nous, allez plutôt voir le W3C pour ça ».</p>
<p> </p>
<p>Le HTML5 dont nous allons parler ici désigne trois choses :</p>
<ul>
<li>l'évolution de HTML 4, avec une syntaxe assouplie, une nouvelle sémantique et l'introduction des <strong>microdata</strong>,</li>
<li>de nouvelles interactions utilisateur, avec les nouveaux formulaires, la géolocalisation et le multimédia</li>
<li>l'arrivée des applications Web avec des API JavaScript comme <strong>Websocket</strong> pour la communication temps réel, la gestion du hors ligne, le stockage d'informations côté client et <strong>XMLHTTPRequest 2</strong></li>
</ul>
<p>Nous avons choisi ces sujets car ce sont que vous pourrez utiliser en production le plus immédiatement, en assurant une compatibilité avec tous les navigateurs depuis Internet Explorer 6.</p>
<h3>Quand HTML5 sera-t-il prêt ?</h3>
<p>Réponse courte : maintenant, et depuis plusieurs années déjà. Lisez la réponse longue pour comprendre de quoi HTML5 est fait.</p>
<h4>2022 ?</h4>
<p>A l’époque où le WHATWG commença à travailler sur la spécification en 2004, Ian Hickson, l’éditeur de la spécification, avait annoncé une date qui est restée longtemps dans la mémoire des journalistes : 2022. Il faut bien comprendre qu’il faisait référence aux critères du W3C : des spécifications complètes, finalisées, acceptées, et surtout avec au moins deux implémentations complètement interopérables. Cela semble raisonnable à première vue, mais concrètement ce niveau de qualité repousse très loin les dates de finalisation. Par exemple après 10 ans d’existence et d’utilisation en production, CSS 2.1 vient à peine d’atteindre le niveau final des spécifications : <em>« Recomendation »</em>. Et malgré cela il n’existe pas encore deux navigateurs implémentant <strong>toutes</strong> les fonctionnalités, et sûrement pas de manière identique. HTML 4.01 lui par contre a bien atteint le niveau <em>« Recomendation »</em> il y a 10 ans, et même si il n’y a pas de test unitaire pour le prouver on peut considérer que plusieurs navigateurs interprètent le même code de la même manière. Mais cela ne change pourtant rien au quotidien des développeurs Web qui doivent assurer le support sur les navigateurs qui ont ignoré ou mal interprété les spécifications.</p>
<h4>2011 ?</h4>
<p>La mauvaise nouvelle, c’est que l’on vient de voir avec CSS2.1 et HTML 4.01 que l’état d’une spécification du W3C ne signifiait pas grand chose pour le développeur web. La bonne nouvelle, c’est que pour HTML5 non plus l’état de la spécification n’a pas beaucoup d’importance ! Ce qui compte vraiment c’est :</p>
<ul>
<li>la stabilité des implémentations navigateur,</li>
<li>la possibilité d'émuler la fonctionnalité sur les anciens navigateurs,</li>
<li>à défaut, l'acceptation d'une page non améliorée pour les anciens navigateurs</li>
</ul>
<p>Autre grande nouvelle : HTML5 n’est pas fait d’un bloc. La spécification se découpe en de nombreuses fonctionnalités séparées, n’ayant parfois pas de lien entre elles, et chaque navigateur avance sur ses sujets favoris. Concrètement il y a aujourd’hui une bonne dizaine de nouvelles fonctionnalités qui sont à la fois suffisamment stables, suffisamment répandues et en même temps simulables sur les anciens navigateurs, ce sont celles que nous allons couvrir rapidement aujourd’hui et qui nous font dire que HTML5 est déjà là.</p>
<h4>1999 ?</h4>
<p>Poussons le raisonnement jusqu’au bout : en découpant HTML5 en spécifications séparées, on arrive à des conclusions étranges ; Internet Explorer 5.5, sorti en 1999 est compatible avec certaines fonctionnalités HTML5 comme le <em>drag and drop</em>. Évidemment cela s’est fait dans l’autre sens, c’est à dire qu’un des premiers travaux du WHATWG a été de coucher sur le papier des fonctionnalités comme le <em>drag and drop</em> qui n’avaient jamais été écrites. Cet exemple est là pour illustrer que la question du « quand » n’a pas vraiment de sens.</p>
<p><strong>Conclusion</strong> : des fonctionnalités majeures associées à HTML5 sont disponibles dès maintenant pour des environnements de production, y compris avec la contrainte du support d’anciens navigateurs.</p>
<h3>La philosophie</h3>
<p>HTML5 est né dans le conflit et avec la volonté d’être pragmatique pour une adoption rapide. Cela lui confère un certain nombre de principes :</p>
<ul>
<li><strong>Un assouplissement des règles</strong>. En réaction aux travaux sur XHTML 2 qui impliquait la syntaxe stricte de XML dans les pages HTML, HTML5 est clairement permissif et enlève les attributs inutiles, les <code>doctype</code>s compliqués, enlève l'obligation d'utiliser des <em title=""">quotes </em> et autorise les variations de casse.</li>
<li><strong>L'unification des règles de <em>parsing</em></strong>. Les navigateurs respectant HTML5 implémentent le même algorithme d'interprétation du texte HTML pour construire le DOM, c'est à dire la représentation finale du document accessible en CSS et en JavaScript.</li>
<li><strong>La rétro-compatibilité</strong>. Au contraire de ce sur quoi s'engageait le W3C à l'époque, les nouvelles fonctionnalités sont introduites avec des syntaxes permettant au parc de navigateurs actuels (en partant de Internet Explorer 6) de ne pas « casser » les pages.</li>
<li><strong>Formalisation de l'existant</strong>. Le WHATWG a commencé par écrire les spécifications de fonctionnalités jamais documentées auparavant et pourtant implémentées au prix de longues heures de <em>reverse engineering </em> par tous les navigateurs. Cela va de fonctionnalités complètes comme le <em>drag and drop </em>à des attributs DOM comme <code>.innerHTML</code>.</li>
</ul>
<p>On le voit, HTML5 a été pensé avec beaucoup de pragmatisme pour une adoption immédiate. Cela est du à la volonté des fabricants de navigateurs de faire avancer le Web. Une fois ces bases saines établies, ils ont maintenant les coudées franches pour rattraper les applications natives en terme de fonctionnalités.</p>
<h2>Après HTML4</h2>
<p>Avant l’animation 2D, la vidéo ou des piles de JavaScript pour faire du logiciel, HTML5, c’est d’abord du code HTML ! La sémantique est revue, la syntaxe évolue et l’accessibilité est prise en compte.</p>
<h3>Nouvelle syntaxe</h3>
<p>La modification la plus visible est bien sur l’introduction du nouveau <code>doctype</code> :</p>
<pre><code><!doctype html></code></pre>
<p>Cela simplifie les choses et on est même en droit de se demander à quoi servaient les <code>doctype</code>s 4.01 <em>strict </em>ou <em>transitional</em>. Pour la petite histoire, Internet Explorer 6 n’utilisait cette instruction que pour savoir dans quel mode de rendu CSS il fallait se mettre (<em>quirksmode</em> ou mode standard).</p>
<p>Autre simplification : la déclaration de l’encodage de la page.</p>
<pre><code><meta charset="utf-8"></code></pre>
<p>Certains attributs comme <code>type</code> dont l’absence est universellement bien interprétée peuvent maintenant disparaître :</p>
<pre><code><script> alert('Iceberg droit devant'); </script>
<style> #titanic { float: none; } </style>
</code></pre>
<h3>nouvelle sémantique</h3>
<p>Plusieurs balises disparaissent carrément car elles ont été accusées de mélanger le fond et la forme. Citons celles qui étaient le plus couramment utilisé : <code>font</code>, <code>center</code>, <code>strike</code>, <code>u</code>, <code>big</code>, <code>frame</code>, <code>frameset</code> ou encore <code>acronym</code> et <code>applet </code>ainsi qu’une petite dizaine d’autres.</p>
<p>Les attributs suivants sont également frappés d’obsolescence :</p>
<p><code>longdesc</code> et <code>name</code> sur la balise <code>img</code>, <code>scope</code> sur la balise <code>td</code>, <code>classid</code> sur la balise <code>object</code>, <code>target</code> sur la balise <code>link</code> ainsi qu’une dizaine d’autres.</p>
<p>Ont été remplacés pour laisser la place à CSS :</p>
<p><code>align</code>, <code>bgcolor</code>, <code>background</code>, <code>border</code>, <code>cellpadding</code> et <code>cellspacing</code> sur <code>table</code>, <code>height</code> sur <code>td</code> et <code>th</code>, <code>marginheight</code> et <code>scrolling</code> sur <code>iframe</code> ainsi qu’une vingtaine d’autres.</p>
<p>D’autres changent subtilement de valeur sémantique. Il n’y aurait pas assez de place dans ces colonnes pour ré-expliquer le sens de chacune d’entre elles, aussi prenons la plus célèbre : <code>strong</code>. Avant, elle était censé désigner un mot qui devait se dire avec emphase (sur lequel on mettrait l’accent à l’oral). Cette fonction étant maintenant dévolue à la balise <code>em</code>, <code>strong</code> veut maintenant dire « mot important », ce qui colle avec l’usage effectif qui en était fait aujourd’hui, notamment en référencement.</p>
<p>Enfin et surtout, de nouvelles balises apparaissent, les plus importantes étant les balises dite <strong>de section </strong>: <code>section</code>, <code>article</code>, <code>aside</code>, <code>header</code>, <code>footer</code>. Elle sont utiles pour préciser un peu les zones de votre site que vous entouriez auparavant d’une <code>div</code>, faute de mieux. Vous pouvez utiliser le chemin de décision suivant pour déterminer la nature de votre balise :</p>
<ul>
<li>le contenu est il indépendant du site et peut il être exporté sans ce contexte ? Par exemple un article de blog ou une fiche produit. Dans ce cas là, la balise <code>article</code> est toute indiquée</li>
<li>le contenu mérite au moins un titre et regroupe des informations sur la même thématique ? La balise section est faite pour ça et vous permet de marquer par exemple des onglets, des modules de news, une zone de commentaires …</li>
<li><code>aside</code>, <code>footer</code> et <code>header</code> peuvent être utilisées pour marquer des zones particulières dans les sections que nous venons de voir : un groupe de titres pour <code>header</code>, des métadonnées (<em>copyright</em>, auteur, référence…) pour le <code>footer</code> et des informations complémentaires pour <code>aside</code> (autres articles, produits similaires...)</li>
<li>La <code>div</code> reste d'actualité pour tous les besoins non couverts</li>
</ul>
<p>Citons également:</p>
<ul>
<li>la balise <code>nav</code> qui marque les liens de navigation de la page (et non d'une <code>section</code>, <code>footer</code> étant plus indiqué pour cela),</li>
<li><code>time</code> qui permet de marquer de manière formelle une date (exemple PHP : <code><time datetime="<?= date( DATE_W3C); ?>">Maintenant !</time></code>).</li>
<li><code>figure</code> et <code>figcaption</code> pour les illustrations et leur description (regardez le code source de cet article)</li>
</ul>
<p>On le voit, ces nouvelles balises peuvent être mises à profit principalement pour les sites à contenu éditorial (journaux, réseaux sociaux…) voire les sites de e-commerce. Le problème que vous rencontrerez sera que dans les anciennes version d’Internet Explorer vous ne pourrez pas appliquer de style à ces éléments (mettre un <code>display:block</code> sur les balises sectionnantes par exemple). Pour cela il suffit d’un court fix Javascript que vous pourrez trouver sur le net en tapant <em>HTML5 shiv</em>.</p>
<h3>microdata</h3>
<p>Microdata, présenté plus en détails ailleurs dans ce numéro (NDR : le numéro en question étant payant, je vous propose d’aller directement sur <a href="http://schema.org/docs/gs.html#microdata_why">le site édité par Google</a>), se propose de remplacer RDFa et microformat. Si vous ne connaissez pas ces projets, il s’agit simplement de marquer sa page d’une manière beaucoup plus précise et libre que ce que la spécification HTML5 peut prévoir. Si par exemple votre site liste des événements, des recettes de cuisine, des personnes ou des avis vous allez pouvoir indiquer très précisément dans votre page où se trouvent les informations intéressantes. Concrètement vous pouvez définir vos propres vocabulaires ou vous rendre sur <a href="http://schema.org">http://schema.org</a> (maintenu par Google, Bing et Yahoo!) et choisir le vocabulaire adapté à ce que vous voulez décrire. Puis à l’aide de 3 attributs (<code>itemscope</code>, <code>itemtype</code> et <code>itemprop</code>), vous marquez les éléments importants. Par exemple nous allons marquer précisément une page présentant un profil. On commence par marquer la zone avec <code>itemscope</code> :</p>
<pre><code><div itemscope>../..
</div></code></pre>
<p>On déclare ensuite le vocabulaire auquel on va se référer grâce à <code>itemtype</code> et une <abbr>URL</abbr>. Ici on va prendre le format <code>hCard</code>, probablement le plus répandu des formats. Notez que l’url pointe sur le site microformats : microformat et microdata sont compatibles car définir un vocabulaire ne consiste jamais qu’à lister des clés et leurs valeurs possibles.</p>
<pre><code><div itemscope itemtype="http://microformats.org/profile/hcard">
../..
</div>
</code></pre>
<p>Enfin avec <code>itemprop</code> vous marquez chaque information de votre markup pour indiquer où se trouve la valeur de chaque partie du vocabulaire.</p>
<figure>
<pre><code><div itemscope itemtype="http://microformats.org/profile/hcard">
Vous pouvez contacter <span itemprop="given-name">JP Vincent</span>
pour toute question HTML5 sur
<a itemprop="email" href="mailto:jp@braincracking.org">ce mail</a>
../..
</div>
</code></pre>
<figcaption>marquage d'une page de contact avec le standard microdata et un vocabulaire microformat</figcaption>
</figure>
<p>Un programme tel que ceux de Google ou certains plugins navigateur vont lire le markup et associer le nom avec le mail. </p>
<h3>ARIA</h3>
<p>ARIA est une spécification axée sur l’accessibilité et permet de proposer des interfaces riches qui restent accessibles aux utilisateurs de technologie d’assistance. A son niveau le plus basique vous pouvez au moins indiquer les zones principales grâce à l’attribut <code>role</code>: les liens de navigations avec <code>role="navigation"</code>, le contenu principal avec <code>role="main"</code>, les mentions légales avec <code>role="contentinfo"</code>, le titre ou le logo avec <code>role="banner"</code>…</p>
<p>A un niveau un peu plus évolué, vous pouvez marquer des interactions plus complexes et néanmoins répandues comme des barres de progression (<code>role="progressbar"</code>), des curseurs (<code>role="scrollbar"</code>, <code>role="slider"</code>), des menus, des boîtes de dialogue (<code>role="dialog"</code>). C’est très utile aux technologies d’assistance pour comprendre des zones de l’écran qui sont plus dynamiques que jamais : imaginez que vous ayez un chat en ligne en Web, comme dans l’exemple suivant.</p>
<figure>
<pre><code><ol>
<li>KevinDu75: Kikoo ^__^
</ol>
</code></pre>
<figcaption>Markup d'une chatroom</figcaption>
</figure>
<p>Sans indication particulière, un lecteur d’écran va lire une seule fois ce code, alors qu’il y a sûrement une routine JavaScript qui met à jour régulièrement cette zone. Avec ARIA, il suffit de rajouter le rôle correspondant (d’après la spécification, c’est <code>log</code>) à cette fonctionnalité classique, et les lecteurs se mettront à lire automatiquement tout dernier élément ajouté au DOM</p>
<figure>
<pre><code><ol role="log">
<li>KevinDu75: Kikoo ^__^
<li>DarkBlackWarrior666 : LOL !!!!!
</ol>
</code></pre>
<figcaption>Marquage ARIA de la zone de chat</figcaption>
</figure>
<p>La bonne nouvelle c’est que certaines librairies JavaScript y compris certains plugins jQuery rajoutent automatiquement des attributs ARIA lorsqu’ils vous proposent des widgets un peu compliqués. C’est à vous de faire attention à ce que l’option soit bien proposée pour garantir une meilleure accessibilité de vos interfaces.</p>
<h2>Interaction utilisateur</h2>
<h3>Les nouveaux formulaires</h3>
<p>Les formulaires HTML ont été largement dépoussiérés.
Auparavant on avait surtout des champs de type texte ou mot de passe. HTML5 définit une série de nouveaux types qui se rencontrent couramment sur les formulaires : <code>url</code>, <code>mail</code>, <code>search</code>, <code>date</code>, <code>number</code>&helip; Quel est l’intérêt ? Il offre aux navigateurs et aux technologies d’assistance des points d’attache pour améliorer l’expérience utilisateur : changement de clavier virtuel sur les mobiles pour faciliter la saisie, apparition d’interfaces spécialisées pour sélectionner facilement des dates ou des chiffres …
[caption id=”attachment_926” align=”aligncenter” width=”238” caption=”les adaptations des navigateurs aux champs date et tel”]<img class="size-full" src="http://94.23.51.58/jpvincent/blog/wp-content/uploads/2012/01/html5_intro_figure_04.png" alt="" /><img class="size-full wp-image-926" title="type_date" src="http://94.23.51.58/jpvincent/blog/wp-content/uploads/2012/01/html5_intro_figure_03.png" alt="" width="238" height="208" />[/caption]
La validation des données a été standardisée. Jusqu’ici les développeurs empilaient une bonne couche de Javascript par dessus chaque formulaire. Ici simplement avec du HTML vous pouvez demander au navigateur d’exécuter un certain nombre de règles de validation et d’afficher des indications à l’utilisateur pour remplir sans se tromper son formulaire.
La simplification de certains comportements jusqu’ici scriptés : mettre le <em>caret </em>automatiquement sur certains formulaires avec l’attribut <code>autofocus</code>, mettre un texte de suggestion par défaut dans les champs texte qui apparaît ou disparaît avec <code>placeholder</code>.</p>
<p>Pour le développeur Web coincé entre PHP et JavaScript il y a ici une vraie opportunité : la validation des données doit bien sur rester côté serveur, et il y a maintenant une manière très simple de réutiliser le même code entre le client et le serveur. Prenons une classe de validation en PHP simpliste que l’on voudrait utiliser, comme ci-après.</p>
<figure>
<pre><code><?php
class validation {
// d'abord on met public l'expression rationnelle pour l'utiliser plus tard
public static $regMail="^[w-]+(?:.[w-]+)*@(?:[w-]+.)+[a-zA-Z]{2,7}$";
public static $regNickname="#^[a-zA-Z0-9àáâãäåèéêëìíîïòóôõöùúûüýýÿçñ _-.']{2,50}$#i";
// fonctions appellées par le serveur
static function isValidMail( $email ) {
return preg_match("/".self::$regMail."/",$email));
}
static function isValidNickname( $nickname ) {
return preg_match("/".self::$regNickname ."/",$nickname));
}
}
</pre></code>
<figcaption>une classe statique PHP de validation des données</figcaption>
</figure>
On voit dans le listing 4 que l'on a isolé les expressions régulières utilisées pour valider le formulaire dans des variables statiques, et nous allons en fait les réutiliser dans le HTML. En HTML5 nous avons l'attribut <code>pattern</code> pour donner des expressions régulières au navigateur qu'il va exécuter pour vérifier l'entrée utilisateur, comme ci-après.
<figure>
<pre><code><form>
<label >Votre mail : <input type="email" pattern="<?= validation:: $regMail ?>" required />
</label>
<label >Votre surnom : <input type="text" pattern="<?= validation:: $regNickname ?"> required />
</label>
</form>
</pre></code>
<figcaption>Transmission des expressions régulières de validation au navigateur</figcaption>
</figure>
[caption id="attachment_928" align="aligncenter" width="234" caption="interface de validation de formulaire, sous Chrome"]<a href="http://94.23.51.58/jpvincent/blog/wp-content/uploads/2012/01/html5_intro_figure_05.png"><img class="size-full wp-image-928" title="html5_intro_figure_05" src="http://94.23.51.58/jpvincent/blog/wp-content/uploads/2012/01/html5_intro_figure_05.png" alt="" width="234" height="93" /></a>[/caption]
Sans avoir tapé de CSS ou de javascript, certains navigateurs afficheront l'équivalent de la figure 5 à l'utilisateur. Vous trouverez quelques librairies Javascript qui permettent d'émuler la même chose sur tous les navigateurs.
<h3>vidéo native</h3>
Les balises <code>audio</code> et <code>video</code> ont été calquées sur le modèle de la balise <code>img</code> : permettre très simplement d'insérer du multimédia dans une page Web. La promesse est bien sur de se passer de Flash pour lire de la vidéo, et d'offrir à vos utilisateurs plus de fluidité (pas de démarrage de Flash ou d'un autre plugin), une meilleure utilisation du CPU sur les environnements que Flash n'adressait pas (Mac OS, Linux, les mobiles) et une interface par défaut qu'ils connaissent déjà. Pour le développeur, pouvoir utiliser JavaScript et CSS pour contrôler une vidéo est très naturel et promet donc de belles intégrations.
Tempérons cela par plusieurs points :
<ul>
<li>les implémentations actuelles des navigateurs sont encore un cran en dessous de ce que permet Flash. Notoirement mettre une interface en CSS en vrai plein écran n'est encore possible sur aucun navigateur, il faut pour cela attendre une autre spécification : la <em>Fullscreen API</em>, en cours de test chez Firefox et Chrome</li>
<li>la guerre des codecs continue de faire rage : le standard de l'industrie vidéo est h.264 et ils n'ont aucune envie d'adapter tous les outils de leur chaîne de production pour le Web. De l'autre côté WebM, pourtant offert à la communauté par Google, ne peut être décodé que sur Firefox, Chrome et Opéra et on attend depuis un an qu'Adobe fasse de même pour que les sites Web n'aient pas à stocker en deux encodages leurs vidéos.</li>
<li>le W3C n'a pas l'intention d'adresser le marché particulier du <em>streaming </em>payant ou des vidéos protégées, car c'est contraire à la philosophie d'ouverture du Web. Cela signifie que pour ces marchés Flash restera très longtemps la seule technologie utilisable.</li>
</ul>
En bref, les éléments multimédia sont déjà indispensables sur des environnements où Flash n'est pas présent, et vous pouvez déjà essayer de l'intégrer en complément de Flash sur vos sites Web, mais il est peu probable que ce soit votre seul lecteur.
<h3>Géolocalisation</h3>
La géolocalisation permet à une page Web de savoir (après autorisation de l'utilisateur) où celui-ci se trouve. Au contraire de la géolocalisation par IP qui peut au mieux vous donner le nom de la ville de votre point d'entrée sur le Net (en ADSL, cela peut être une erreur de 5 kms), les implémentations navigateur se basent sur des données matérielles plus précises :
<ul>
<li>sur bureau, l'adresse MAC de la carte Wifi est envoyée sur les systèmes de Google (pour Firefox, Opéra et Chrome) ou de Microsoft, qui ont scanné à grande échelle les zones les plus densément peuplées, et qui y ont associé des coordonnées GPS. En gros, si la « streetview » de Google Maps ou de Bing est disponible pour votre quartier, vous serez repéré précisément (de l'ordre de la centaine de mètres)</li>
<li>sur mobile, c'est le GPS embarqué qui est directement interrogé. A défaut de GPS, c'est la triangulation par antenne relai.</li>
</ul>
Dans tous les cas si aucune donnée n'est disponible, c'est au final l'adresse IP qui est utilisée, avec l'imprécision que l'on sait.
Voici un exemple d'implémentation qui essaye d'utiliser l'API du navigateur, mais qui à défaut va utiliser les solutions traditionnelles de détection par IP :
<figure>
<pre><code>// test du support navigateur
if( navigator.geolocation ) {
// appel asynchrone à l'API
navigator.geolocation.getCurrentPosition( function( position ) {
// l'objet position un sous-objet contient toutes les informations
console.log( position.coords.latitude, position.coords.longitude );
});
} else {
// retour à une solution moins précise
var oXHR = new XMLHTTPRequest();
// appel d'un script PHP qui va récupérer l'IP et trouver la ville
oXHR.open('GET', '/getlocation.php');
// on peut supposer que le script répond avec des coordonnées
oXHR.onload = function( e ) {
console.log( e.currentTarget.responseText );
};
oXHR.send();
}
</pre></code>
<figcaption>Géolocalisation native, avec méthode de secours traditionnelle</figcaption>
</figure>
Les solutions de correspondance IP / ville sont généralement payantes, même si il en existe des gratuites. Elle se présentent sous la forme de script et de base de données à installer chez soi et à exploiter en PHP. Dans l'un ou l'autre cas vous n'êtes bien sur absolument pas certain de la véracité des données. La géolocalisation des navigateurs permet à tout le moins d'avoir une solution IP gratuite, et dans la plupart des cas d'obtenir des informations beaucoup plus précises.
L'API prévoit aussi de demander des coordonnées plus précises, de pouvoir surveiller les déplacements et de fournir des données de vitesse, d'altitude, d'orientation par rapport au nord. Certaines implémentations peuvent également y rajouter la ville ou l'adresse !
Le fait que géolocalisation existe, et marche depuis déjà deux ans est majeur pour les technologies du Web, car c'est la première fois qu'une page Web peut interagir avec le matériel de l'utilisateur, privilège réservé jusqu'ici aux applications natives.
<h2>L'ère des applications Web</h2>
<h3>Websocket et Server Sent Events</h3>
WebSocket implémente ce que les développeurs Web devaient jusque là simuler : de la communication temps réel bi-directionnelle entre le client et le serveur. Le protocole HTTP montre en effet ses limites lorsqu'il s'agit d'attendre des informations venant du serveur ou d'écrire un jeu vidéo avec un flux d'informations continu client / serveur. Chaque envoi d'information est encapsulé par parfois plusieurs ko de headers tandis qu'avec websocket il ne s'agit plus que de quelques octect superflus. De plus la connection reste ouverte en permanence ce qui réduit très fortement la latence, chose indispensable pour des applications aussi réactives que des jeux. Il y a un article dans ce numéro qui trait plus en profondeur de WebSocket
Le problème de WebSocket pour le développeur PHP, c'est que cela remet en cause beaucoup de choses côté serveur : les serveurs WebSocket d'aujourd'hui sont écrits dans des langages tels que JavaScript (sur serveur Node.js) qui sont taillés pour exécuter du code de manière asynchrone et qui supportent en théorie des milliers de connexions ouvertes simultanément. Il existe bien une implémentation WebSocket en PHP, mais on ne sait pas vraiment quelle charge elle peut tenir en production, l'empreinte mémoire et la consommation CPU d'un processus PHP étant généralement assez gourmande : après tout pour tenir un socket ouvert, il n'y a pas d'autre moyen qu'une boucle infinie entrecoupée de <code>sleep()</code> et PHP n'a pas été pensé dans ce sens !
A côté de WebSocket, il y a la spécification « Server Sent Events », qui elle ne permet d'avoir que la communication du serveur au client, ce qui peut suffire à la plupart des applications : surveiller un flux twitter, un cours boursier, avoir un dashboard etc … Pour envoyer des informations au serveur, il reste bien sur <code>XMLHTTPRequest</code>, qui est un peu plus lent que WebSocket et qui crée un nouveau thread sur le serveur. En fait SSE remplace efficacement et formalise la technique dite « forever iframe » où PHP et JavaScript gardaient la connexion ouverte via une <code>iframe</code> où du HTML était régulièrement injecté, même lorsqu'aucun événement ne devait être envoyé.
L'avantage de Server Sent Events, c'est qu'elle permet de réutiliser pratiquement le même code côté PHP, avec quelques ajouts que nous allons commenter, tout en optimisant le trafic réseau et en formalisant les échanges de données. Regardons d'abord comment on faisait en PHP pour maintenir la connexion ouverte par une iframe :
<figure>
<pre><code><?php
// désactivation des caches navigateurs et proxys
header('Cache-Control: no-cache');
// boucle infinie
while(true) {
// méthode métier de récupération des données
$data = my_get_data();
// encodage en JSON pour interprétation directe en JavaScript
$data = json_encode( $data );
// on fait exécuter une fonction JavaScript qui contient nos données
print '<script> fonction_rappel('. $data .'); </script>';
// même si les données sont vides, la connexion est maintenue car du HTML est envoyé
flush();
// on laisse respirer le CPU avant la prochaine itération
sleep(1);
}
</pre></code>
<figcaption>technique de <em>forever iframe</em>, côté PHP</figcaption>
</figure>
Voici maintenant les lignes que nous devrions ajouter pour renvoyer des informations à un navigateur supportant SSE :
<figure>
<pre><code><?php
// détection du support par le navigateur
<strong>$hasSSE = ($_SERVER['HTTP_ACCEPT'] === 'text/event-stream');</strong>
// désactivation des caches navigateurs et proxys
header('Cache-Control: no-cache');
// on informe le navigateur qu'on va lui répondre au format SSE
if( $hasSSE === true)
<strong>header('Content-Type: text/event-stream');</strong>
// boucle infinie
while(true) {
// méthode métier de récupération des données
$data = my_get_data();
// encodage en JSON pour interprétation directe en JavaScript
$data = json_encode( $data );
if( $hasSSE === true)
// le standard prévoit que les données arrivent après la chaîne data:
<strong>print 'data: '. $data;</strong>
else
// on fait exécuter une fonction JavaScript qui contient nos données
print '<script> fonction_rappel('. $data .'); </script>';
// le standard sépare les données sur plusieurs lignes par un double retour chariot
<strong>print PHP_EOL.PHP_EOL;</strong>
flush();
sleep(1);
}
</pre></code>
<figcaption>Ajout du code pour supporter Server Sent Event</figcaption>
</figure>
On le voit, on envoie simplement un entête supplémentaire, puis chaque ligne de donnée est préfixée par <span style="text-decoration: underline;">data:</span> (avec ou sans espace), et les blocs de données sont séparées par un <strong>double retour chariot</strong>.
Côté client il suffit d'instancier l'objet <code>EventSource</code> avec l'URL du serveur et d'écouter les événements, comme ici :
<figure>
<pre><code>// on se branche sur le PHP
var connexion = new EventSource('/get_data.php');
// programmation événementielle
connexion.onmessage = function( e ) {
// la propriété data de l'événement contient directement la chaîne JSON qu'on a créé en PHP
console.log( e.data );
};
</pre></code>
<figcaption>Utilisation côté client de Server Sent Event</figcaption>
</figure>
L'API va beaucoup plus loin et en plus de la reconnexion automatique gère également :
<ul>
<li>les tickets, généralement un marqueur de temps ou un numéro de ligne qui peut servir de repère à PHP lorsque le navigateur se reconnecte,</li>
<li>des événements nommés, qui permettent de créer des catégories de dialogue,</li>
<li>des événements comme les erreurs, l'ouverture et la fermeture de la connexion</li>
</ul>
Il existe d'hors et déjà des librairies JavaScript permettant de simuler l'API Server Sent Events sur tous les navigateurs, et donc de réutiliser votre code PHP.
<h3>Offline</h3>
De plus en plus d'utilisateurs sont en situation de mobilité lorsqu'ils consultent des sites Web, qu'ils soient sur smartphone ou qu'ils utilisent un laptop dans un café avec une connexion Wifi défectueuse ou inexistante. Le grand avantage qu'ont les applications dites natives, c'est que l'interface se lance immédiatement, qu'il y ait du réseau ou pas. Hors certaines applications Web comme les jeux <em>casual</em> n'ont parfois pas du tout besoin de connexion, tandis que d'autres comme les webmails n'ont besoin d'une connexion que pour aller chercher de nouvelles données.
La gestion de l'offline vise à mettre les applications Web au même niveau que les applications natives en :
<ul>
<li>ne faisant télécharger qu'une fois pour toute l'interface et le cœur applicatif,</li>
<li>donnant de quoi gérer les versions de l'application,</li>
<li>permettant de réagir et détecter les situations de déconnexion.</li>
</ul>
Concrètement vous allez référencer un fichier <code>.manifest</code> dans votre HTML :
<pre><code><!doctype html>
<html manifest="mapage.manifest">
…
</code></pre>
Puis vous allez servir ce fichier (avec un entête type mime "<code>text/manifest</code>") qui référence plusieurs choses :
<ul>
<li>les dépendance (CSS, JS, images..) à garder pour une utilisation <em>offline</em> (section <code>CACHE</code>) ,</li>
<li>les URLs qui doivent absolument accéder au réseau (section <code>NETWORK</code>),</li>
<li>les pages par défaut lorsque le réseau ne répond pas (section <code>FALLBACK</code>)</li>
</ul>
L'exemple suivant serait une application Web qui n'aurait que deux dépendances, qui irait chercher le reste sur le réseau, et qui affiche la page <code>offline.html</code> pour toute autre requête sans réseau :
<figure>
<pre><code>CACHE MANIFEST
# v1.0
CACHE
css/styles.css
js/main.js
FALLBACK:
/ offline.html
NETWORK:
*
</pre></code>
<figcaption>fichier <code>offline.manifest</code></figcaption>
</figure>
La déclaration est simple, c'est dans la programmation métier de votre application que les choses se compliquent. Par défaut les navigateurs vont donc afficher la version qu'ils ont en local, puis aller vérifier si le manifeste de cache a changé ne serait ce que d'un octet (c'est pour cela qu'on met généralement en commentaire le numéro de version de l'application). Si c'est le cas, il va télécharger de manière transparente toutes les ressources, qu'elles aient changé ou non, et envoyer un événement Javascript pour dire qu'il a mis à jour le cœur applicatif. Il appartient au développeur de gérer ces états de transition entre deux versions de la même application. Il peut être aidé en cela par une série d'événement JavaScript et de fonctions situées dans l'objet global <code>applicationCache</code>, et qui lui donneront l'état de la mise à jour et un contrôle sur celle ci.
Notons par ailleurs que l'on a accès à la propriété <code>window.navigator.onLine</code> (valeur booléenne), censée donner l'état de la connexion, et aux événements <code>ononline </code>et <code>onoffline</code>, qui avertissent du changement d'état. Malheureusement seul Internet Explorer, et encore à partir de la version 8, implémente de manière utile cette propriété et ces événements.
<h3>XHR 2</h3>
<code>XMLHTTPRequest</code> a été la base technique à ce que l'on a appelé le « Web 2.0 » et permet de faire des requêtes asynchrones au serveur pour récupérer ou envoyer des informations. Dans HTML5, la version 2 déjà implémentée dans la plupart des navigateurs donne plus de contrôle et d'informations sur les requêtes :
<ul>
<li>indications de progression : si la taille totale de votre envoi de fichier ou de votre téléchargement est connue, vous avez accès à l’évènement <code>progress</code>, et aux propriétés <code>total</code> et <code>loaded</code>. Vous pouvez maintenant afficher à votre utilisateur de vraies barres de progression.</li>
<li>création de formulaires à la volée : l'API <code>FormData</code> permet de créer ou de modifier des formulaires à la volée et le navigateur les formate en mode <code>POST</code>. Ceci permet de gérer facilement l'envoi de fichiers envoyés en <em>drag and drop</em> depuis le bureau ou en sélection multiple, le tout sans rechargement de page</li>
</ul>
En bref, là aussi cette API modernise les applications Web en faisant évoluer un des outils principaux des développeurs.
<h3>Local Storage</h3>
Que l'on gère un site Web classique avec des formulaires sur plusieurs pages ou une application Web lourde type google Docs, on a besoin de stocker les informations que l'utilisateur génère, sans forcément passer par le réseau pour stocker ces informations sur le serveur. On pouvait jusqu'ici utiliser les cookies, mais on était limité en taille (4ko tout compris) et on pouvait dégrader sérieusement les performances : sur la plupart des sites les cookies sont envoyés avec chaque requête HTTP et une page Web moyenne est composée de 80 requêtes HTTP, soit un trafic réseau énorme et surtout inutile. La solution préconisée par PHP est d'ailleurs de ne stocker dans le cookie qu'un court identifiant de session, pour retrouver côté serveur les données.
« Web Storage » permet soit de faire tampon, soit d'éviter carrément le trafic réseau ou la gestion de session en offrant aux développeurs un espace pour stocker au moins 5 Mo de données. Utilisez l'API <code>sessionStorage</code> pour un stockage temporaire (vidé à la fermeture de la page) ou <code>localStorage</code> pour un stockage permanent. Dans les deux cas vous pouvez stocker des paires clé/valeur très simplement :
<figure>
<pre><code>localStorage.setItem('locale', 'fr_FR');
console.log( localStorage.getItem('locale') );
localStorage.removeItem( 'locale');
</pre></code>
<figcaption>définir, récupérer et supprimer une valeur côté client</figcaption>
</figure>
Seules les chaînes de caractère sont acceptées, donc si vous avez besoin de stocker des données plus structurées, utilisez simplement JSON. Si vous avez besoin d'utiliser cette fonctionnalité sur tous les navigateurs, sachez que vous pouvez utiliser Flash ou des techniques propriétaires de chez Microsoft pour y stocker jusqu'à 100ko de données. Il existe déjà plusieurs petites librairies qui font cette abstraction pour vous.
<h2>Le futur</h2>
Les navigateurs veulent clairement concurrencer les applications dites natives, voici ce sur quoi ils planchent.
<strong>La 3D </strong>: avec WebGL, qui permet donc de faire des jeux ou des environnements immersifs le tout en 3D. L'interaction avec le DOM permet même de réutiliser d'autres composants : on peut par exemple utiliser en texture un élément <code><video></code> ou le contenu d'une <code><div></code> sur des éléments de décor. Certaines sociétés éditrices de jeux vidéos console ou PC travaillent déjà sur cette techno pour trouver de nouveaux canaux de distribution et toucher plus de monde, en pariant sur une plus large diffusion des navigateurs et sur l'accélération matérielle généralisée d'ici 2 à 3 ans.
<strong>Le multithread </strong>: la spécification <code>Web Workers</code> permet d'exécuter du javascript dans un processus différent du navigateur, et même d'exploiter un second processeur. Cela permet d'avoir des applications avec des calculs lourds telles de la manipulation de photo ou de vidéo, des jeux vidéo ou des applications scientifiques.
<strong>L'accès au matériel</strong> : la géolocalisation permet déjà d'accéder au GPS des mobiles, <code>getUserMedia</code> permet d'accéder à la caméra, et <code>DeviceOrientation</code> pointe sur les informations du gyroscope. Il y a déjà des implémentations sur Firefox, Chrome et certains mobiles.
<strong>Le peer to peer </strong>: des expérimentations ont été menées et permettent de faire dialoguer directement deux navigateurs de deux machines différentes sans passer par un serveur pour maintenir la connexion. Oui le prochain Skype pourrait être écrit en JavaScript.
<strong>L'accès au bureau </strong>: plein écran, notifications, contacts … il y a plusieurs expérimentations autour de ces fonctionnalités qui vont permettre aux pages, pardon aux applications Web de s'intégrer complètement à votre OS, que vous soyez sur mobile ou sur PC classique.
Et je n'ai listé ici que les API qui ont au moins une implémentation, fusse-t-elle expérimentale. L'idée vous l'aurez compris est de donner les pleins pouvoirs aux « sites » Web qui sont aujourd'hui les interfaces privilégiées par le grand public. Cela ne se fait pas sans soulever des questions sur la sécurité, mais le W3C veille et chaque API est prévue pour passer par une demande de permission à l'utilisateur.
<h2>Conclusion</h2>
HTML5 et les navigateurs apportent toutes ces nouvelles capacités qui rivalisent avec les applications natives. Avec la puissance de diffusion du Web qui est de fait multi-plateforme, HTML5 termine le travail commencé par PHP et Linux car les logiciels de demain pourront être écrits avec des langages non propriétaires. C'est une grande victoire pour le monde du libre.
Références :
<h3>Sur Internet</h3>
<ul>
<li><em>http://braincracking.org/</em> – [FR] le site de l'auteur : veille technologique sur HTML5, Javascript et les performances Web,</li>
<li><em>http://livre-html5.com/</em> – [FR] le site du livre HTML5, avec les démos,</li>
<li><a href="http://www.html5-css3.fr/"><em>http://html5-css3.fr/</em></a> – [FR] formation et tutoriaux à HTML5 et CSS3,</li>
<li><a href="http://alsacreations.com/"><em>http://alsacreations.com/</em></a> – [FR] tutoriaux, forums et actualité des technos frontend,</li>
<li><em><a href="http://html5doctor.com/">http://html5doctor.com/</a> </em>– [EN] site d'information sur la sémantique de HTML5,</li>
<li>http://www.html5rocks.com/<em> </em>– [EN] maintenu par Google : tutoriaux et présentation générale avec démos de HTML5 au sens large.</li>
</ul>
</pre></figure></pre></figure></pre></figure></pre></figure></pre></figure></pre></figure></pre></figure></pre></figure>L’article suivant est la retranscription de l’article que j’ai écrit pour le magazine PHP Solutions, dans le numéro hors série de 2011 consacré à HTML5. Le monde compliqué du droit d’auteur veut que ce texte ne m’appartienne pas, c’est donc avec leur permission express que j’en fait bénéficier mes lecteurs (oui toi). Toute reproduction est donc interdite.Usage avancé des fonctions JavaScript2011-11-17T15:04:21+00:002011-11-17T15:04:21+00:00/2011/11/17/usage-avance-des-fonctions-javascript<p>Cet article est un complément à l’article sur <a href="http://braincracking.org/?p=883">les 3 fondamentaux de JavaScript</a>, il vaut mieux être déjà à l’aise avec JavaScript avant de crier au scandale en voyant ce qu’on peut en faire. Pour reprendre un bon mot de quelqu’un qui avait assisté à ma <a href="http://www.slideshare.net/jpvincent/javascript-fondamentaux-et-oop">conférence sur JavaScript</a> :</p>
<p>[caption id=”attachment_897” align=”aligncenter” width=”542” caption=”javascript == la pornstar des langages de dev: souple, puissant, tu lui fait faire ce que tu veux, et ça peut finir bien crade.”]<a href="https://twitter.com/#!/pierreca/status/134629949985398785"><img class="size-full wp-image-897 " title="pornstar" src="http://braincracking.org/wp-content/uploads/2011/11/pornstar.png" alt="" width="542" height="214" /></a>[/caption]</p>
<p>Admettons donc que vous ayez digéré sans problème les portées et les fonctions, passons à deux choses vraiment particulières à JavaScript :</p>
<ol>
<li>le renvoi de fonction qui permet de belles optimisations et qui ouvre la voie à des patterns que les amoureux de la théorie du langage apprécieront,</li>
<li>une implémentation de <strong>classe statique</strong>, pour reprendre le terme utilisé en PHP ou en Java.</li>
</ol>
<p>Et enfin nous verrons une proposition d’implémentation de deux <em>design pattern</em> célèbres et particulièrement utiles en JavaScript : <em>Singleton</em> et <em>Factory</em>.
<!--more--></p>
<h1>Classe statique</h1>
<p>Pour rappel, en PHP et dans d’autres langages, une propriété ou une méthode statique peut être appelée sans que la classe n’ait été instanciée pour créer un objet. C’est généralement là que l’on range les constantes ou les fonctions utilitaires par exemple. En JavaScript, tout étant objet y compris les fonctions, cela se fait assez naturellement :</p>
<pre><code>// constructeur
var <strong>myClass</strong> = function () {
};
<strong>myClass.staticMethod</strong> = function() {
console.log('OK');
};
// que voit on au niveau global ?
myClass.staticMethod(); // OK
</code></pre>
<p>Regardez la manière dont est définie <code>staticMethod</code> : on la range directement dans la fonction <code>myClass</code> ! Elle est donc directement disponible sans passer par la création d’un objet. Comparons d’ailleurs avec une définition de méthode d’objet comme on l’a vu dans les paragraphes précédents pour bien comprendre où sont disponibles ces nouvelles méthodes de classe.</p>
<pre><code>// constructeur
var myClass = function () {
return {
publicMethod:function() {
console.log('OK');
}
}
};
myClass.staticMethod = function() {
console.log('OK');
};
// que voit on au niveau global ?
myClass.publicMethod(); // Error
myClass.staticMethod(); // OK
// que voit l'instance ?
myObject = myClass();
myObject.publicMethod(); // OK
myObject.staticMethod(); // Error
</code></pre>
<p>Si vous exécutez ce code dans votre console, vous allez voir où se produisent les erreurs :</p>
<ul>
<li>vous ne pouvez pas accéder à <code>publicMethod</code> sans avoir d'abord instancié <code>myClass</code>,</li>
<li>l'instance de <code>myClass</code> ne contient pas <code>staticMethod</code> car celle ci est directement disponible à partir du nom de la classe.</li>
</ul>
<h1>Renvoi de fonction</h1>
<p>Une fonction peut se redéfinir elle même quand elle s’exécute. Ca a l’air sale dit comme ça, mais nous allons voir un cas concret où cela est bien utile. Imaginez que vous construisez une mini-librairie dont une des fonctions permet de se rattacher à un événement du DOM. Pour supporter tous les navigateurs, il y a deux méthodes aux noms distincts et aux arguments légèrement différents que vous pouvez encapsuler dans une fonction en faisant un simple <code>if</code>.</p>
<pre><code>var onDOMEvent =
function( el, ev, callback) {
// le monde de Microsoft
<strong>if(document.body.attachEvent){</strong>
el.attachEvent('on'+ev, callback);
// le monde du W3C
} else {
el.addEventListener( ev, callback, false);
}
};
</code></pre>
<p>Cette fonction marche très bien, mais à chaque fois qu’elle est exécutée, le test sur la condition aussi est exécutée. Si vous faîtes une librairie vous vous devez de ne pas ralentir les développeurs qui l’utiliseraient. Hors cette fonction pourrait très bien être appelée des centaines de fois, exécutant ainsi inutilement du code. Pour optimiser cela, nous allons redéfinir la fonction à la volée lors de sa première exécution.</p>
<pre><code>var onDOMEvent =
function( ) {
if(document.body.attachEvent) {
<strong>return function</strong>(element, event, callback) {
element.attachEvent('on'+ event, callback);
};
} else {
<strong>return function</strong>(element, event, callback) {
element.addEventListener( event, callback);
};
}
}<strong>()</strong>;
</code></pre>
<p>Comme vous le voyez :</p>
<ul>
<li>cette fonction est auto-exécutée grâce aux deux parenthèses finales, et n'a plus besoin des arguments puisqu'elle ne sera plus jamais exécutée après.</li>
<li>le <code>if</code> reste, mais il ne sera exécuté qu'une seule fois.</li>
<li>la fonction <strong>renvoie des fonctions anonymes</strong> contenant les codes spécifiques au navigateur. Notez que ces fonctions attendent toujours les mêmes paramètres.</li>
<li>lorsque <code>onDOMEvent()</code> sera appelé, seul l'un ou l'autre corps de fonction sera exécuté, nous avons atteint notre objectif</li>
</ul>
<p>C’est une technique d’optimisation pas forcément évidente à intégrer mais qui donne de très bons résultats. Cela irait un peu trop loin pour cet article mais si vous avez l’âme mathématique, cherchez donc sur le Web comment calculer la suite de Fibonacci en JavaScript, avec et sans “memoization” (<a href="http://fr.wikipedia.org/wiki/M%C3%A9moization">Wikipedia</a>). Vous pouvez également créer des fonctions spécialisées qui capturent certains paramètres pour vous éviter d’avoir à les préciser à chaque fois, technique connue sous le nom de <em>currying</em> (voir ce <a href="http://ejohn.org/blog/partial-functions-in-javascript/">post de John Resig</a> à ce sujet).
Autre cas concret d’école d’utilisation de cette technique. Partons du code suivant qui boucle sur un petit tableau d’objet et qui rattache l’événement <code>onload</code> à une fonction anonyme.</p>
<pre><code>var queries = [ new XHR('url1'), new XHR('url2'), new XHR('url3')];
for(var i = 0; i < queries.length; i++) {
queries[i].onload = function() {
console.log( i ); // référence
}
}
</code></pre>
<p>Observez bien la valeur de <code>i</code> : notre fonction anonyme crée une portée, le parseur javascript ne voit pas <code>i</code> dans cette fonction, il remonte donc d’un niveau pour la trouver. Jusqu’ici tout va bien notre variable est bien référencée. Pourtant lorsque l’événement <code>onload</code> est appelé par le navigateur, nous avons une petite surprise:</p>
<pre><code>queries[ 0 ].onload(); // 3!
queries[ 1 ].onload(); // 3!
queries[ 2 ].onload(); // 3!</code></pre>
<p><code> </code>
L’interpréteur JavaScript a correctement fait son boulot : la fonction <code>onload</code> voit bien <code>i</code> (sinon nous aurions eu <code>undefined</code>), mais c’est une <strong>référence</strong> vers la variable, pas une copie de sa valeur ! Hors <code>onload</code> n’est appelé qu’après que la boucle se soit terminée, et <code>i</code> a été incrémentée entretemps. Pour fixer cela, nous allons utiliser deux choses :</p>
<ol>
<li>l'auto-exécution, qui va nous permettre de copier la valeur de <code>i</code></li>
<li>le renvoi de fonction pour que <code>onload</code> soit toujours une fonction</li>
</ol>
<p>Attention, ça peut piquer les yeux :</p>
<pre><code>for(var i = 0; i < queries.length; i++) {
queries[i].onload = function(i) {
<strong>return function() { </strong>
console.log( i ); // valeur
};
}<strong>(i);</strong> // exécution immédiate
}
// plus tard ...
queries[ 0 ].onload(); // 0
queries[ 1 ].onload(); // 1
queries[ 2 ].onload(); // 2
</code></pre>
<p>Essayez de suivre le chemin de l’interpréteur :</p>
<ul>
<li><code>i</code> est donné à la fonction anonyme auto-exécutante</li>
<li>le paramètre de cette première fonction anonyme s'appelle aussi <code>i</code> : dans cette portée locale, <code>i</code> a pour valeur 0 (pour la première passe)</li>
<li>la fonction renvoyée embarque toute la portée avec elle et n'a donc que la <strong>valeur</strong> de ce nouveau <code>i</code>, qui ne bougera plus</li>
</ul>
<p>Pour info, ce cas d’école est souvent posée lors des entretiens d’embauche si on veut vérifier que vous avez bien compris les portées.</p>
<h1>Implémenter des design pattern</h1>
<p>En combinant namespace (voir l’article <a href="http://braincracking.org/?p=89">JavaScript pour les développeurs PHP</a>), portée maîtrisée, espace privé et émulation d’objets, nous allons implémenter le <em>design pattern Factory</em>. Factory ou Singleton sont très intéressants en JavaScript, notamment pour les <em>widgets</em> (type jQuery UI) : vous pouvez vous retrouver sur des pages où vous ne savez pas si le JavaScript d’un Widget s’est déjà exécuté sur tel élément du DOM. Pour optimiser, vous ne voulez pas recréer systématiquement le Widget mais plutôt <strong>créer une nouvelle instance ou récupérer l’instance en cours</strong>. Vous avez donc besoin :</p>
<ol>
<li>d'interdire la création directe d'un objet</li>
<li>de passer par une fonction qui va faire la vérification pour vous et vous renvoyer une instance</li>
</ol>
<p>Commençons par créer notre espace de travail, ainsi que notre namespace:</p>
<pre><code>(function(){
// création ou récupération du namespace et du sous-namespace
MY = window.MY || {};
MY.utils = MY.utils || {};
// constructeur
MY.utils.XHR=function( url ){
console.log( url );
};
})();
</code></pre>
<p>A ce stade nous avonc une classe accessible de l’extérieur avec <code>new MY.utils.XHR( url );</code>. C’est bien mais pas top, nous voudrions passer par une fonction qui va vérifier s’il n’existe pas déjà une instance avec ce même paramètre URL. Nous allons déclencher une erreur en cas d’appel direct (<a href="http://www.php.net/manual/fr/language.oop5.patterns.php#language.oop5.patterns.singleton">comme en PHP</a>) et prévoir une fonction <code>getInstance( url )</code> qui va se rattacher au namespace de la fonction constructeur.</p>
<pre><code>(function(){
// création ou récupération du namespace et du sous-namespace
MY = window.MY || {};
MY.utils = MY.utils || {};
// constructeur
MY.utils.XHR=function( url ){
<strong>throw new Error('please use MY.utils.XHR.getInstance()');</strong>
};
// factory
<strong>MY.utils.XHR.getInstance</strong> = function( url ) {
};
})();
</code></pre>
<p>Enfin nous allons introduire une variable privée qui va contenir la liste de nos instances (un objet <code>currentInstances</code> avec en index l’url et en valeur l’instance). Nous allons également rendre privé notre vrai constructeur.</p>
<pre><code>(function(){
// constructeur
MY.utils.XHR=function( url ){
throw new Error('please use MY.utils.XHR.getInstance()');
};
//constructeur privé
<strong>var XHR = function( url ){</strong>
console.log( url );
};
// liste privée d'instances
<strong>var currentInstances = {};</strong>
// factory
MY.utils.XHR.getInstance = function( url ) {
// déjà créé ? on renvoie l'instance
if(currentInstances[url]) {
return <strong>currentInstances[url]</strong>;
// on crée, on enregistre, on renvoie
} else {
return currentInstances[url] = <strong>new XHR(url);</strong>
}
};
})();
</code></pre>
<p>Telle quelle, cette implémentation permet déjà de créer une factory pour des objets qui ont besoin d’être unique mais qui n’ont qu’un seul paramètre (<code>id</code> d’un objet DOM, URL …). La vraie raison d’être d’une Factory, c’est de gérer des objets complexes à instancier, à vous donc d’étendre ses fonctionnalités. Les puristes auront remarqué que l’objet renvoyé n’était pas du type <code>MY.utils.XHR</code>, et qu’on ne pouvait donc pas faire de vérification avec <code>instanceof</code> du type de l’objet. Honnêtement je ne connais pas de bon moyen de le faire, à vous de voir si c’est un manque dans votre code.
Vous voilà paré à écrire du code un peu plus maintenable et mieux rangé, et vous pourrez crâner dans les dîners en ville en racontant vos exploits de codeur JS orienté objet.</p>
<h1>Conclusion</h1>
<ul>
<li>JavaScript a des concepts différents des langages majeurs et devient extrêmement important sur votre CV. Prenez le temps de l'apprendre</li>
<li>Les librairies telles que jQuery ne sont pas faites pour couvrir les cas que nous venons de voir. jQuery permet d'utiliser le DOM sereinement, pas d'organiser votre code proprement.</li>
<li>J'ai essayé d'être pratique, mais lire un post de blog ne suffira jamais pour comprendre des concepts de programmation : codez !</li>
</ul>Cet article est un complément à l’article sur les 3 fondamentaux de JavaScript, il vaut mieux être déjà à l’aise avec JavaScript avant de crier au scandale en voyant ce qu’on peut en faire. Pour reprendre un bon mot de quelqu’un qui avait assisté à ma conférence sur JavaScript :JavaScript : 3 fondamentaux2011-11-16T15:04:21+00:002011-11-16T15:04:21+00:00/2011/11/16/javascript-3-fondamentaux<p>Après quelques années à écrire dans un langage, on finit facilement par oublier les premières difficultés que l’on avait rencontrées. Et à force de faire de la veille, de l’autoformation et de parler entre experts dans des conférences, j’ai un peu quitté la réalité de la majorité des équipes Web.</p>
<p>Maintenant que je suis consultant indépendant je retourne dans des équipes qui avaient autre chose à faire que de se demander si on a le droit de parler de classe en JavaScript, quelle est la bonne définition d’une <em>closure</em>, ou quelles sont les fonctionnalités de EcmaScript 5 qui auraient du rester dans Ecmascript.Next.</p>
<p>J’avais déjà parlé sur ce blog de <a href="http://braincracking.org/?p=89">JavaScript et la programmation orienté objet pour les développeurs PHP</a>, nous allons explorer ici les <strong>3 notions fondamentales de JavaScript</strong> qui sont probablement les plus grosses sources de bugs, d’incompréhension et de frustration pour le développeur Web moyen. Et qui accessoirement sont la base d’une programmation plus évoluée par la suite.</p>
<h1>JavaScript est différent : apprenez le</h1>
<p>Le monde du développement Web semble dominé par les langages dérivés de la syntaxe du C, PHP en tête, avec des paradigmes qui se ressemblent. Forcément en abordant JavaScript dont la syntaxe n’est pas vraiment révolutionnaire, on est tenté d’appliquer la même logique. Mais c’est oublier un peu vite que ce langage a été créé il y a déjà 15 ans, quand Java était seulement à mode et pas encore ultra dominant comme aujourd’hui, et qu’il est principalement l’héritier de langages comme Erlang et Lisp, aujourd’hui très peu connus. En fait le mot Java dans JavaScript a été rajouté pour des raisons commerciales, et seuls quelques concepts comme la syntaxe et je crois la gestion des dates ont contribué à former JavaScript. JavaScript n’est donc qu’un cousin éloigné des langages <em>mainstream</em>.</p>
<p>Le maître mot de ses concepteurs semble avoir été la versatilité. Nous allons voir qu’en explorant seulement 3 bases <em>à priori</em> simples, il est possible d’obtenir à peu près n’importe quoi, ce que les grands maître de JavaScript s’amusent tous les jours à faire. En attendant de passer dans la catégorie des maîtres, il faut déjà maîtriser ces bases pour faciliter son travail au quotidien.</p>
<p>Nous allons donc nous baser sur EcmaScript 3 (le javascript de IE6-7-8) dont les trois fondamentaux sont :</p>
<ul>
<li>La portée des variables ( <code>var + function</code>)</li>
<li>Les fonctions</li>
<li>Le contexte (<code>this</code>)</li>
</ul>
<p>Commençons par la portée.
<!--more--></p>
<h1>La portée des variables</h1>
<h2>La théorie</h2>
<p>La portée, c’est ce que voit l’interpréteur à un moment donné de l’exécution du code. Ouvrez un fichier JS, tapez le mot clé <code>debugger;</code> où bon vous semble, démarrez firebug ou votre outil préféré et cherchez l’onglet “Espions” ou “Watch”.</p>
<p>[caption id=”attachment_884” align=”aligncenter” width=”490”]<a href="http://braincracking.org/wp-content/uploads/2011/11/firebug-javascript-2.gif"><img class="size-full wp-image-884" title="Onglet Watch de Firebug" alt="" src="http://braincracking.org/wp-content/uploads/2011/11/firebug-javascript-2.gif" width="490" height="232" /></a> L’onglet Watch ou Espions de Firebug[/caption]</p>
<p>La règle pour comprendre comment est créée une portée est très simple :</p>
<p style="text-align: center;">1 function = 1 portée</p>
<p>Concrètement vous avez la portée globale où tout se trouve (dans un navigateur, c’est <code>window</code>), puis à chaque fonction que vous créez, vous créez une nouvelle portée, et celles ci peuvent s’imbriquer à l’infini. Chaque fois que vous précédez une assignation par le mot clé <code>var</code>, la variable n’est visible qu’à partir de cette portée.</p>
<pre><code>test1 = function() {
var x = 1;
test2 = function() {
var x = 2;
test3 = function() {
var x = 3;
console.log(x); // <strong>3</strong>
}();
}();
console.log(x); // <strong>1</strong>
}();
console.log(x); // <strong>undefined</strong>
</code></pre>
<p>Si vous exécutez le code précédent dans votre console JavaScript, vous constaterez 3 choses :</p>
<ul>
<li>le <code>console.log</code> le plus profond affiche bien sur la dernière valeur de <code>x</code>, à savoir 3. Dans ce troisième niveau, on a écrasé les valeurs précédentes de <code>x</code>.</li>
<li>malgré cet écrasement à un niveau inférieur, le second <code>console.log</code> (qui affiche 1) montre bien que les nouvelles valeurs de <code>x</code> ne sont pas visibles depuis la première fonction.</li>
<li>enfin le dernier <code>console.log</code> est fait au niveau global, en dehors des définitions de fonction. Ici <code>x</code> n'existe même pas.</li>
</ul>
<h2>La pratique</h2>
<p>Nous avons vu comment le couple <code>var</code> + <code>function</code> permet de définir une portée et empêche le code en dehors de la portée de voir certaines variables. Pourquoi ne pas tout mettre au niveau global ? Pour des raison d’organisation du code et de maintenabilité ! Imaginez le code suivant :</p>
<pre><code>function genericFunctionName() {
for(<strong>i</strong> = 0; i < myArray.length; i++) {
<strong>console.log(i);</strong>
}
}
for(<strong>i</strong> = 0; i < 10; i++) {
genericFunctionName();
}
</code></pre>
<p>Nous avons simplement une fonction qui parcourt un tableau fictif, puis nous appelons cette fonction 10 fois. N’exécutez surtout pas cela dans votre navigateur, car <strong>c’est une boucle infinie</strong> !</p>
<p>En fait les deux boucles <strong>for</strong> utilisent le même nom de variable (<strong>i</strong>) pour compter les tours. C’est un nom extrêmement commun et ça ne poserait pas de problème si les deux boucles ne pointaient pas sur <strong>la même variable</strong> ! Fixons cela.</p>
<pre><code>function genericFunctionName() {
for( <strong>var i</strong> = 0; i < myArray.length; i++) {
console.log(i);
}
}
for(i = 0; i < 10; i++) {
genericFunctionName();
}
</code></pre>
<p>Dans la fonction, nous avons rajouté le mot clé <code>var</code> pour spécifier que la variable n’appartenait qu’à cette fonction. Les deux bouclent ne pointent plus sur la même variable même si leurs noms sont les mêmes. C’est très important pour le développeur de savoir qu’il est le seul maître sur les variables qu’il crée pour créer un code particulier. Ceci nous donne la première application concrète des portées.</p>
<h2>Créer son espace de travail sécurisé</h2>
<p>En tant que développeur, vous allez créer ou modifier du code sur des pages dont vous ne maîtrisez pas l’environnement. Entre l’historique du code, vos collègues et les publicités ou les widgets, beaucoup de variables sont créées ou écrasées au niveau global sans que vous ne puissiez le prévoir. Au milieu de cette tourmente, je vous propose de vous créer un petit havre de paix :</p>
<pre><code><strong>(function() { </strong>
<strong>}()) </strong>
</code></pre>
<p>Je vous conseille de démarrer tout fichier javascript ou tout code entre balise <code><script></code> par la première ligne et de toujours terminer par la dernière. Ce bout de code vous permet de poser les bases de votre portée et de créer des variables qui n’appartiennent qu’à vous, sans pour autant s’isoler du reste du monde.</p>
<pre><code>(function() {
var privateVariable = true;
window.init = function() {
console.log( privateVariable );
}
}())
init(); // <strong>true</strong>
console.log(privateVariable); // <strong>undefined</strong> </code></pre>
<p>Exécutez ce code, et vous verrez que <code>privateVariable</code> n’est pas accessible de l’extérieur, mais que le code défini à l’intérieur permet d’y accéder. Très pratique en environnement hostile. Vous remarquerez aussi que l’on rattache la fonction <code>init</code> directement à la portée globale, afin qu’elle soit elle aussi visible de l’extérieur.</p>
<h1>Les fonctions</h1>
<h2>C'était si simple ...</h2>
<p>Il est facile de créer une fonction et de l’exécuter. Les syntaxes que vous utilisez au quotidien sont celles ci :</p>
<pre><code>// création v1
function action() {}
// création v2
action = function() {};
// exécution
action();
</code></pre>
<p>Simple non ?</p>
<h2>La fausse bonne idée</h2>
<p>Au fait quelle est la différence entre les deux manières de déclarer une fonction ? Pour la première ( <code>function nom()</code> ) le compilateur JavaScript extrait toutes les définitions et les met à disposition au niveau global AVANT de commencer l’exécution. Concrètement vous pouvez même exécuter la fonction avant qu’elle ne soit créée dans la source.</p>
<pre><code>// exécution avant la définition !
action();
// création
function action() {}
</code></pre>
<p>Cool ? Pas vraiment : c’est systématiquement la dernière déclaration dans la source qui est prise en compte par les interpréteurs JS. Vous ne pouvez pas conditionner la définition des fonctions. Par exemple le code suivant ne va pas s’exécuter comme vous pourriez le penser :</p>
<pre><code>
if( 1 == 1 ) {
// l'exécution du code passe ici ...
function action() {
console.log('a == 1');
}
} else {
// ... pourtant le compilateur se souvient de cette fonction
function action() {
console.log('a != 1');
}
}
action(); // <strong>a != 1 </strong>
</code></pre>
<p>Lors de l’exécution, l’interpréteur passe bien dans le premier <code>if</code> et pourtant c’est la seconde définition qui est retenue, uniquement parce qu’elle arrive en dernier dans la source.
Et malgré cela, contrairement à d’autres langages, la déclaration d’une fonction n’est valable que pour la portée en cours et ses descendantes. Dans le code suivant, la fonction <code>action</code> n’est pas visible au niveau global :
<code></code></p>
<pre>(function() {
function action() {
console.log('action');
}
}())
action(); // undefined</pre>
<p>La manière traditionnelle de déclarer les fonctions est donc assez trompeuse et de ce fait tombe doucement en désuétude. Pour des débutants on utilisera plutôt la seconde méthode qui a le mérite de pouvoir <strong>explicitement</strong> choisir la portée de ses fonctions, au même titre qu’une variable</p>
<pre><code>var action = function() {};</code></pre>
<p>En ce qui concerne le compilateur cette syntaxe est une variable avec pointeur sur une fonction anonyme. Le désavantage est que lorsque vous utilisez un débogueur pas à pas, la pile d’appels (<em>call stack</em>) ne vous affiche pas le nom des fonctions (l’anonymat sur Internet existe donc). Vous pouvez retrouver le nom de la variable assignée simplement en cliquant sur la fonction et en regardant dans la source.</p>
<h2>Particularités Javascript</h2>
<p>JavaScript a rajouté plusieurs particularités aux fonctions qui les rendent terriblement puissantes et flexibles. En fait toutes les constructions de code un peu élaborées telles que l’orientation objet ou l’application de <em>design pattern</em> se basent sur ces spécificités. Nous entrons donc dans le coeur de JavaScript.</p>
<h3>Auto-exécution</h3>
<p>En rajoutant simplement une paire de parenthèses après une déclaration, la fonction va immédiatement être exécutée.</p>
<pre><code>var autoInit = function() {
console.log('hello world');
}<strong>()</strong>;
// <strong>hello world</strong>
</code></pre>
<p>Comme nous l’avons vu tout à l’heure, cela vous permettra principalement de créer des portées, pour protéger l’ensemble de votre script, et nous allons voir dans une minute que vous pouvez aussi l’utiliser dans certains cas particuliers.</p>
<h3>Classe ou fonction ?</h3>
<p>En JavaScript, tout est objet. Un objet n’est jamais qu’une collection de clés et de valeurs, et les valeurs peuvent être tout et n’importe quoi y compris d’autres objets. Lorsqu’une fonction est créée, automatiquement JavaScript y rajoute la propriété <code>prototype</code>. Tapez le code suivant dans votre console JavaScript :</p>
<pre><code>var myFunction = function() {};
console.log( myFunction.prototype ); // <strong>Object</strong>
</code></pre>
<p>Nous avons donc un objet <code>myFunction.prototype</code> et nous pouvons directement y accéder pour y rajouter des valeurs, comme d’autres fonctions par exemple. Ensuite pour accéder à ces fonctions, nous allons utiliser le mot clé <code>new</code>.</p>
<pre><code>// cette innocente fonction devient un constructeur
var myClass = function () {
<strong>this.</strong>publicVariable = 0;
};
// accès au prototype
<strong>myClass.prototype</strong> = {
decrement:function() {
console.log( --this.publicVariable );
},
increment:function() {
console.log( ++this.publicVariable );
}
};
myObject = <strong>new myClass</strong>();
myObject.decrement(); // -1
myObject.decrement(); // -2
myObject2 = <strong>new myClass</strong>();
myObject2.increment(); // 1
myObject2.increment(); // 2
</code></pre>
<p>Entre nous, nous venons de créer quelque chose qui ressemble furieusement à une classe, avec une instanciation (<code>new myClass</code>), des variables propres à chaque instance ( <code>this.publicVariable</code> ) et un constructeur (le corps de la fonction).</p>
<h3>Orienté objet ?</h3>
<p>Si vous trouvez bizarre qu’une fonction se transforme soudainement en classe puis en objet, attendez de voir la suite. Toujours grâce à la notion du tout objet de JavaScript, nous pouvons également faire de l’héritage ! Là encore, en utilisant <code>.prototype</code>, nous allons créer une nouvelle fonction (ou classe) qui va hériter des méthodes de la première. Outre la déclaration de la nouvelle fonction, cela ne va prendre que deux lignes.</p>
<pre><code>mySubClass = function() {
// maintenant on part de 10 au lieu de 0
this.publicVariable = 10;
};
<strong>mySubClass.prototype = myClass.prototype;
mySubClass.prototype.constructor = mySubClass;</strong>
myObject2 = new mySubClass();
myObject2.increment(); // 11
myObject2.increment(); // 12
</code></pre>
<p>La première ligne après la déclaration de fonction fait pointer l’objet <code>.prototype</code> de notre nouvelle fonction vers l’objet <code>.prototype</code> de la classe mère, empruntant ainsi toutes ses propriétés. La seconde ligne est là pour rectifier un défaut de l’écrasement de <code>.prototype</code> : on refait pointer <code>.prototype.constructor</code> (encore une propriété automatiquement créée pour toutes les fonctions) vers le bon constructeur.</p>
<h3>Slip ou caleçon ?</h3>
<p>Pardon je voulais dire : pour les classes, vous êtes plutôt prototype ou closure ? On vient de voir que grâce à <code>.prototype</code> on pouvait émuler une classe en JavaScript. Les fonctions peuvent également renvoyer des objets simples contenant des propriétés et des méthodes, et la manière de les utiliser se rapprochera toujours terriblement d’une classe.</p>
<pre><code>myClass = function () {
// variable privée !
var privateVariable = 0;
// méthodes publiques
<strong>return { </strong>
decrement:function() {
console.log( --privateVariable );
},
increment:function() {
console.log( ++privateVariable );
}
<strong>}</strong>
};
myObject = myClass();
myObject.decrement(); // -1
myObject.decrement(); // -2
myObject2 = myClass();
myObject2.increment(); // 1
myObject2.increment(); // 2
</code></pre>
<p>Plusieurs différences par rapport à la version prototype :</p>
<ul>
<li>Notez le <code>return {...}</code> dans la définition de la fonction : vous pouvez y mettre exactement ce que vous auriez mis dans le prototype.</li>
<li>Vous pouvez utiliser la portée de la fonction constructeur pour avoir des variables privées (ici <code>var privateVariable</code>), alors qu'il n'est <strong>pas possible d'avoir des variables privées avec prototype</strong></li>
<li>Lorsque vous instanciez votre classe, vous n'avez plus besoin d'utiliser <code>new myClass()</code>, un simple appel sans <code>new</code> suffit.</li>
<li>Le mot clé <code>this</code> désigne l'objet renvoyé, alors qu'avec prototype il désignait le corps de la fonction constructeur. Généralement avec closure on n'utilise plus du tout <code>this</code></li>
<li>la création des fonction est évalué à chaque fois que vous invoquez la fonction constructeur, tandis que ce n'est fait qu'une seule fois avec prototype. <strong>N'utilisez jamais closure avec des objets qui peuvent être instanciés des centaines de fois</strong>.</li>
</ul>
<p>Voilà pour l’essentiel sur les fonctions : avec cela vous maîtrisez déjà leur portée et vous pouvez commencer à développer orienté objet, ce qui va vous permettre d’éviter beaucoup de bug et d’avoir un code plus maintenable.</p>
<h1>Le contexte ou this</h1>
<p><code>this</code> est le petit mot clé qui perd beaucoup de développeurs, notamment lorsque l’on est habitué aux dérivés du C comme PHP. En JavaScript comme ailleurs, il fait référence au contexte d’exécution, donc le code suivant s’exécutera probablement comme vous vous y attendez :</p>
<pre><code>myClass = function() {
this.id = <strong>'myClass'</strong>;
}
myClass.prototype = {
action:function() {
console.log( <strong>this.id</strong> );
}
};
myObject = new myClass();
myObject.action(); // <strong>'myclass'</strong>
</code></pre>
<p>Ici on a défini dans le constructeur <code>this.id</code> avec une certaine valeur, puis on instancie la classe (<code>new myClass()</code>) et la méthode <code>action()</code> accède tout naturellement à <code>this</code> pour retrouver la valeur de <code>id</code>.
Pourtant lorsque vous voulez exécuter <code>myObject.action</code> au clic sur un élément du DOM, plus rien ne marche comme prévu.</p>
<pre><code>document.body.onclick = myObject.action;
// document.body.id
</code></pre>
<p><code> </code>
Vous pouvez exécuter les deux bouts de code précédent dans votre console JavaScript et cliquer sur la page, vous verrez que <code>this.id</code> n’est plus ‘myClass’ mais fait référence à l’<code>id</code> du <code>body</code>, qui n’existe peut être même pas. Ceci n’est pas particulier au DOM d’ailleurs, imaginez que vous créiez un autre objet qui aurait un événement <code>onfire</code> que vous auriez besoin d’écouter :</p>
<pre><code>myEvent = {
id:'myEvent'
};
myEvent.onfire = myObject.action;
myEvent.onfire(); // 'myEvent' au lieu de 'myClass'
</code></pre>
<p><code> </code>
Lorsque <code>onfire()</code> s’exécute, il exécute bien le corps de la fonction <code>action</code>, mais comme pour les objets du DOM, <strong><code>this</code> fait référence à l’objet d’où est exécutée la fonction</strong>, pas forcément à l’objet d’origine de la fonction.
Ceci est du à la versatilité de JavaScript qui permet de très simplement copier le corps d’une fonction d’un objet à l’autre, alors que dans les langages traditionnels orienté objet il vous faut explicitement faire de l’héritage. Comment fixer cela ? Il y a au moins deux méthodes.</p>
<h2>Changement de contexte (natif)</h2>
<p>La première méthode est native à JavaScript et utilise la fonction <code>.call()</code>. Ici seule la dernière ligne a changé :</p>
<pre><code>myClass = function() {
this.id = 'myClass';
}
myClass.prototype = {
action:function() {
console.log(this.id);
}
};
myObject = new myClass();
myEvent = {
id:'myEvent'
};
myEvent.onfire = myObject.action;
<strong>myEvent.onfire.call( myObject ); // myClass</strong>
</code></pre>
<p><code> </code>
Le premier argument de la fonction <code>.call()</code> est l’objet qui sera utilisé pour <code>this</code>. Ici on redonne à <code>onfire</code> l’objet d’origine de la fonction <code>action</code>.
Le problème avec cette solution, c’est qu’il faudrait que vous soyez certain que TOUS les endroits où votre fonction peut être appelée vont rectifier le contexte d’exécution. Hors ne serait ce que pour les événement natifs du DOM cela n’est pas possible, sauf si vous utilisez une bibliothèque de manière systématique. Donc faire confiance à <code>this</code> n’est raisonnable que si vous maîtrisez parfaitement tout le code qui sera écrit.</p>
<h2>Conservation du contexte (à la main)</h2>
<p>Pour éviter ces problèmes de maintenance, la solution en vogue est de tout simplement ne jamais utiliser <code>this</code> directement et de préférer utiliser la syntaxe closure et une variable privée pour définir ses classes.</p>
<pre><code>myClass = function() {
this.id = 'myClass';
// capture du contexte
<strong>var me = this;</strong>
return {
action:function() {
console.log( <strong>me.id</strong> );
}
}
};
myObject = new myClass();
document.body.onclick = myObject.action;
// 'myClass'
</code></pre>
<p><code> </code>
On voit donc ici :</p>
<ul>
<li>dans le constructeur, on capture le contexte avec une variable privée : <code>var me = this</code></li>
<li>dans la fonction <code>action</code> on n'utilise plus <code>this</code> mais <code>me</code></li>
</ul>
<p>Peu importe l’endroit d’où est lancé la fonction (ici au clic sur la page), et peu importe la valeur réelle de <code>this</code>, on est maintenant certain que l’on aura toujours un accès à l’objet originel grâce à une variable privée. Bien sur <code>this</code> reste utilisable normalement, notamment si vous avez besoin de retrouver l’objet DOM cliqué par exemple.</p>
<p>Pour encore plus de souplesse avec les fonctions de JavaScript, allez voir <a href="http://braincracking.org/?p=896">la deuxième partie de cet article</a>.</p>
<h1>L'essentiel</h1>
<p>Si vous ne deviez retenir que quelques points, les voici :</p>
<ul>
<li>JavaScript a des concepts différents des langages majeurs et devient extrêmement important sur votre CV. Prenez le temps de l'apprendre</li>
<li>Les bibliothèques telles que jQuery ne sont pas faites pour couvrir les cas que nous venons de voir. jQuery permet d'utiliser le DOM sereinement, pas d'organiser votre code proprement.</li>
<li>Lorsque vous créez une classe, choisissez la syntaxe prototype pour la performance ou closure pour les variables privées et la maîtrise du contexte d'exécution</li>
<li>N'utilisez plus la syntaxe <code>function maFonction() {}</code></li>
<li>Utilisez systématiquement la fonction anonyme auto-exécutée ( <code>(function() { var .. }())</code>) en début et fin de fichier ou de <code><script></code></li>
<li>Utilisez systématiquement <code>var</code> pour vos variables, y compris celles qui font référence à des fonctions</li>
</ul>
<p>Dernière chose : j’ai essayé d’être pratique, mais lire un post de blog ne suffit jamais pour comprendre des concepts de programmation : codez !</p>Après quelques années à écrire dans un langage, on finit facilement par oublier les premières difficultés que l’on avait rencontrées. Et à force de faire de la veille, de l’autoformation et de parler entre experts dans des conférences, j’ai un peu quitté la réalité de la majorité des équipes Web.HTML5 maintenant, chapitrage de la conférence2011-03-01T15:04:21+00:002011-03-01T15:04:21+00:00/2011/03/01/html5-maintenant-chapitrage-de-la-conference<p>La vidéo sur la conférence que j‘ai donné à Paris Web 2010 vient de sortir, aussi comme pour la conférence sur <a href="http://braincracking.org/?p=790">les Performances Web</a> je l‘ai chapitré et je vous remets les slides pour que vous vous y retrouviez.</p>
<p>Mes capacités d‘orateur valent ce qu‘elles valent, mais le contenu et le discours restent d‘actualité et devraient permettre je l‘espère à certains développeurs de se décoincer face à HTML5 : les fonctionnalités sont utilisables maintenant en production, pour peu qu‘on sache coder, utiliser des librairies faites pour ça et qu‘on soit suffisamment curieux pour tester tout cela. Les bénéfices sont soit immédiats soit à venir, et il y a certains écueils à connaître.
Le maître mot est : testez ! Dans la plupart des cas vous vous rendrez compte que vous pouvez mettre en production et donc améliorer votre site pour vos utilisateurs.
<!--more--></p>
<h2>Le chapitrage</h2>
<p>La conférence :</p>
<ul id="playlist">
<li><a data-start="5">0:05</a> : bonjour</li>
<li><a data-start="70">1:10</a> : relativisier l'état des specs</li>
<li>l‘exemple de Web Storage
<ul>
<li><a data-start="180">3:00</a> : 7 implémentations, mais utilisable</li>
<li><a data-start="270">4:30</a> : librairies d‘accès à Web Storage</li>
<li><a data-start="340">5:40</a> : Exemples d'utilisation de Web Storage</li>
</ul>
</li>
<li><a data-start="385">6:25</a> : HTML5, c'est gros, et alors ?</li>
<li>La nouvelle sémantique
<ul>
<li><a data-start="430">8:30</a> : le doctype</li>
<li><a data-start="830">9:20</a> : nouvelles balises (<code>nav, article, header</code>, légendes ...</li>
<li><a data-start="810">13:30</a> : microdata</li>
<li><a data-start="975">16:15</a> : nouveaux types de champs de formulaire</li>
</ul>
</li>
<li><a data-start="1135">18:55</a> : les APIs JS
<ul>
<li><a data-start="1165">19:25</a> : est on vraiment dans la nouveauté ? Listing des fonctionnalités qui ont déjà un équivalent</li>
<li><a data-start="1317">21:57</a> : comportement des formulaires, <a href="http://www.alistapart.com/d/forward-thinking-form-validation/enhanced_2.html">démo</a> de librairies sous IE6</li>
<li><a data-start="1440">24:00</a> : intérêt d'utiliser quand même le standard HTML5 pour les formulaires</li>
</ul>
</li>
<li><a data-start="1625">27:05</a> : Géolocalisation navigateur : <a href="http://jsfiddle.net/braincracking/EbeJQ/">démo</a> et intérêt par rapport à la géolocalisation par IP, alternative</li>
<li><a data-start="1920">32:00</a> : SVG : Démo sur IE6 grâce à la <a href="http://raphaeljs.com/analytics.html">librairie raphaelJS</a></li>
<li><a data-start="2060">34:20</a> : La vidéo HTML5 : limites et <a href="http://praegnanz.de/html5video/">librairies</a></li>
<li><a data-start="2190">36:30</a> : Drag and Drop et sélection multiple : démo IE6 (applet, flash), retour d'implémentation en prod, transcodage en HTML5</li>
<li><a data-start="2460">41:10</a> : conclusion : partez sur <a href="http://braincracking.org/?p=764">les standards</a>, utilisez <a href="https://github.com/Modernizr/Modernizr/wiki/HTML5-Cross-browser-Polyfills">les librairies</a>, jouez et testez</li>
</ul>
<p>Vu mon débit de parole lorsque je suis sous le feu de la rampe j‘ai terminé 10 minutes avant l‘heure prévue. La scéance de question / réponses a donc pu être longue et très intéressante :</p>
<ul>
<li>44:10 : la remarque qui tue à propos de l‘accessibilité actuelle de HTML5 par Aurélien Levy (j'ai développé ses argument dans ce post sur <a href="http://braincracking.org/?p=860">l'accessibilité de HTML5</a>). Pour résumer : je maintiens que à part 2 bugs, l‘accessibilité n‘est pas dégradée même pour IE6.</li>
<li>47:30 : remarque sur la surenchère de librairie JS sur les formulaires chez certaines sociétés</li>
<li>49:20 : on revient sur ma mauvaise opinion de l‘implémentation de la vidéo HTML5</li>
<li>51:20 : ARIA sera t il toujours utile ? (oui) </li>
<li>52:00 : on re-discute de l‘accessibilité de HTML5 entre pros</li>
<li>53:45 : HTML5 boilerplate</li>
<li>54:05 : question sur l‘interprétation du doctype HTML5 par IE6 (réponse mal dite : IE n‘a que 2 modes, standard ou quirskmode)</li>
<li>55:25 : inquiétude sur les failles de sécurité des librairies</li>
</ul>
<h2>La vidéo</h2>
<div id="container-player-html5-now">
<a href="http://www.dailymotion.com/video/xh9c18">Vous trouverez la vidéo en ligne sur dailymotion</a>
</div>
<script>
var sPlayerId = 'player-html5-now',
sDailymotionID = 'xh9c18',
oPlayer,
playList = document.getElementById('playlist').getElementsByTagName('A');
BC.loadJS('http://ajax.googleapis.com/ajax/libs/swfobject/2.2/swfobject.js', function() {
var params = { allowScriptAccess: "always" },
atts = { id: sPlayerId };
swfobject.embedSWF('http://www.dailymotion.com/swf/'+sDailymotionID+'?enableApi=1&playerapiid='+sPlayerId,
'container-'+sPlayerId, "600", "400", "9", null, null, params, atts);
});
var onTimeClick = function() {
oPlayer.seekTo( this.getAttribute('data-start') );
};
function onDailymotionPlayerReady() {
oPlayer = document.getElementById(sPlayerId);
for(var i=0; i < playList.length; i++) {
playList[i].onclick = onTimeClick;
}
};
</script>
<h2>Les slides</h2>
<p>Les slides vus à la conférence sont ceux ci :
<a title="HTML5 c'est maintenant" href="http://www.slideshare.net/jpvincent/html5-now-light">HML5 c‘est maintenant (IE6 inclus)</a></p>
<iframe src="//www.slideshare.net/slideshow/embed_code/key/kgLbwxqgFCulmE" width="510" height="420" frameborder="0" marginwidth="0" marginheight="0" scrolling="no" style="border:1px solid #CCC; border-width:1px; margin-bottom:5px; max-width: 100%;" allowfullscreen=""> </iframe>
<div style="margin-bottom:5px"> <strong> <a href="//www.slideshare.net/jpvincent/html5-now-light" title="Html5 now light" target="_blank">Html5 now light</a> </strong> from <strong><a target="_blank" href="//www.slideshare.net/jpvincent">Jean-Pierre Vincent</a></strong> </div>
<p>Vous trouvez 3 fois plus de détails concernant HTML5 dans <a href="http://braincracking.org/?p=597">une autre série de slides</a>, avec notamment le détail de Audio/Video, un exemple de microdata, le détail du problème des nouveaux éléments et du localStorage et une implémentation de géolocalisation. Bref, à regarder au calme chez soi pour se former.</p>
<h2>Une suite ?</h2>
<p>Ma prochaine conf sur HTML5 sera plus petite, mais complémentaire de celle ci : à la <a href="http://www.kiwiparty.fr/">Kiwi Party</a> organisée par Alsacréations, j‘essaierais de montrer qu‘intégrer HTML5 dans son processus de fabrication de site ne devrait rien changer tant qu’on respecte déjà les bonnes pratiques.
En parallèle mon projet actuel tourne uniquement autour du passage à HTML5, j‘aurais l‘occasion de vous en reparler dès qu‘il pourra être rendu public.</p>La vidéo sur la conférence que j‘ai donné à Paris Web 2010 vient de sortir, aussi comme pour la conférence sur les Performances Web je l‘ai chapitré et je vous remets les slides pour que vous vous y retrouviez.