Another NoBoy Life part… (le blog de __MaX__)
Retour au blog <<

:: DEV .:. Parallel downloads et inline script

Jeudi 28 octobre 2010 à 21:46

Terminant une petite optimisation pour la vékat j’avais le blog qui me démangeait afin de partager un peu tout ce que je tente de mettre en place pour faire de cette nouvelle mouture de factor une expérience agréable pour nos utilisateurs.

Bref… ces derniers temps j’ai remarqué que peu de sites utilisent cette technique, et c’est de toute façon compréhensible quand on voit comment ils sont codés à l’arrache.

Quand un navigateur se connecte à un site, il a la possibilité d’effectuer des téléchargements en parallèle (soit simultanément pour ceux qui ne suivent pas), hors il s’avère que les scripts js inclus dans le header d’une page html bloquent tout autre téléchargement. L’expliquer techniquement est toujours difficile pour moi, depuis plus d’un an je cherche un article clair et précis à ce sujet, en vain. Il semblerait que ce soit dut aux spécifications HTTP1.1. Ce qui est sûr c’est que charger vos scripts dans l’entête d’une page donne lieu à une suite de requêtes du genre

Image hosted by uppix.net

[clic clic pour lire les légendes]

A l’appel des scripts, tout autre téléchargement est interrompu jusqu’à obtention de ces fichiers. Dans ce cas de figure tout se produit en 2 secondes, quasi transparent pour l’utilisateur (c’est un test en local). Mais quand on voit aujourd’hui la masse énorme de javascripts chargés par le moindre site, la première requête pour l’utilisateur peut sembler extrêmement longue (surtout à l’heure des connexions haut débit où l’utilisateur n’a plus aucune patience), et à plus forte raison si le serveur est un peu chargé et que les requêtes sur les scripts sont un plus longue que la normale.

La solution ? Charger les scripts juste avant la fin de body. On obtient donc ceci

Image hosted by uppix.net

Tous les téléchargements d’images et autres médias chargés par la page sont appelés dès le début et visiblement chargés en parallèle ; puis sont enfin chargés les scripts.

Cette technique n’accélère pas réellement le temps de chargement de le page. Par contre, l’intégralité de la page est chargée, consultable par l’utilisateur dans le cas où les scripts sont en fin de structure html. Il n’a plus la sensation qu’il attends pour que la page s’affiche, mais que le chargement de la structure et de la partie rédactionnelle est chargée instantanément. Seules les images et animation exécutées en javascript se chargent ensuite.

Ceci est bien sympathique, néanmoins c’est là qu’intervient le problème de l’inline script. L’inline script (ou script en ligne) est en fait tout appel javascript effectué dans le code directement (chose qui est super pas cool quand on écoute les élitistes branlette). Les fameux onclick, onblur avec des jolis javascript:mafonction() dedans. Ce type d’appel nécessite que les scripts contenant ces fonctions soient déjà chargés… par exemple dans le head. C’est un petit peu a l’opposé de l’optimisation que l’on compte faire juste au dessus.

Il faut donc avant de vouloir optimiser les téléchargements parallèles, s’assurer que notre code est compatible à ce genre de changement. Aujourd’hui, jQuery ou Mootools répondent à ces besoins en implémentant des méthodes permettant de binder des événements sur votre structure après le chargement complet de la page ou de la structure (onload / onDomReady). Système réellement agréable quand on a commencé à comprendre le fonctionnement.

Offrant la possibilité d’éradiquer la moindre ligne de script inline et optimisant également le code, ceci permet aussi au développeur de savoir que tous les appels javascript sont effectués uniquement dans les fichiers JS plutôt que d’aller courir à droite à gauche dans ses divers fichiers php, html ou template en s’auto-insultant…  Où ai-je bien pu foutre cette fonction à laquelle je dois rajouter un paramètre. Je ne remercierai jamais assez watt0 de m’avoir fait découvrir cette merveille.

Google a bien compris ce problème, mettant à jour Analytics il y a quelques temps pour une version asynchrone n’impactant aucunement l’utilisateur visitant le site… il reste toujours les régies publicitaires armées de codeurs roumains qui persistent à utiliser du code inline qui ralentit le moindre site profitant de leurs services. (qui est a vrai dire un des gros symptôme de Factornews aujourd’hui).

Conclusion : tout ceci nécessite une rigueur importante lors du développement, mais il est aujourd’hui primordial d’optimiser l’expérience du visiteur… faire du web ergonomique, bourré d’ajax et de petites animations implique des pages plus lourdes, s’il est possible pour vous de limiter les temps d’attente en laissant l’utilisateur profiter de votre contenu sera toujours bénéfique pour vos visites. Il ne faut pas perdre de vue qu’un utilisateur quittera votre site entre 7 à 10 secondes s’il n’a pas perçu une information l’intéressant dans ce laps de temps, si en plus ces 10 secondes sont allouées à charger un javascript…

28 octobre 2010 à 21:53

C’est effectivement une problématique qui va prendre une proportion de plus en plus importante vu l’augmentation du rôle de JS dans les sites modernes.
Par contre je suis étonné de ton étonnement concernant le synchronisme des chargements JS. C’est en fait que le déroulement d’un script B chargé après le script A dépend du script A. Si A n’est pas encore fini de charger, B ne peut pas s’exécuter normalement. Dans javascript il y a script, et c’est là l’enjeu.

Pour ma part, j’annihile toute trace de js et d’attributs js dans mon html et j’ai de plus en plus recours aux solutions de cache proposées par google. Si l’internaute a visité un site avec jQuery@google, c’est déjà 50ko et une requête de moins à s’occuper !

par e-t172
28 octobre 2010 à 22:01

Est-ce que le script bloque également le chargement dans le cas d’un script externe déclaré dans le < head > avec un attribut “defer” activé ? Il me semble que c’est justement l’utilité de cet attribut…

28 octobre 2010 à 22:01

Tu bluffes Martoni, la Vékat est un mythe !

par __MaX__
28 octobre 2010 à 22:02

C’est vrai que je pense pas trop au système de cache google. Faudrait que je me penche dessus. J’ai déjà en projet d’implémenter google minify sur factor pour optimiser les temps de chargement en complément.

Par contre je ne suis pas persuadé que ce que tu dis a propos des scripts js soit vraiment une raison bloquant les téléchargements parallèles. Il suffit de voir l’évolution de firefox qui chargeait vraiment de manière sérialisée les scripts (les un après les autres) jusqu’à la version 3 je crois et qui aujourd’hui charge en parallèle les JS mais bloque tout autre type de requête. Regarde les screenshots, hors le core de jQuery, le reste est chargé en //, et pourtant j’ai des scripts dans mes autres fichiers qui font bien appel a jqCookie ou jUI.

par LeGreg
28 octobre 2010 à 22:40

Personnellement je ne crée pas mes pages moi-même (enfin pas récemment) et donc c’est parfois gênant d’intégrer toutes ces recettes d’optimisation si elles ne font pas partie d’emblée du kit tout fait que l’on utilise (même avec des modules ou plugins qui peuvent avoir l’effet contraire sur l’optimisation). Mais effectivement ce serait bien que les créateurs de modules/gestionnaires de contenu en tiennent compte lors de leur conception (j’aime les efforts de Drupal sur certains points, je finis par être allergique à wordpress par contre).

Quant à la solution d’avoir les scripts cachés par Google, c’est quelque chose qui m’ennuie pour les utilisateurs qui veulent profiter d’un site sans devoir se faire tracker par Google (ou autre compagnie tierce non liée au site visité). Si un script d’analyse de visite peut se désactiver sans souci, les scripts type JQuery ont une influence sur la navigation, affichage correct, etc.

par jye
29 octobre 2010 à 10:35

Alors pour ce qui est du chargement des resources JavaScript, le truc, c’est qu’avant d’avoir totalement téléchargé et parsé le script, le navigateur est incapable de dire ce qu’il y a dedans. Le script peut trés bien contenir un document.write() qui modifie l’arbre DOM ou bien même contenir un location.href qui envoie le user sur une autre page. Si c’est le cas, toutes les resources téléchargées avant ne seront jamais utilisé. Pour éviter ce genre de downloads inutiles (remember modem 28k), les navigateurs téléchargent puis parsent et executent chaque script avant d’enchainer sur le suivant. Résultats: les scripts bloquent le téléchargement du reste des resources et ça a, comme tu l’as remarqué, un impact négatif sur le chargement de ta page.

L’autre truc que tu as remarqué (cf ton lien sur la spec), c’est que les downloads sont souvent 2 par 2. C’est effectivement à cause d’une restriction sur les téléchargements parallèle présent dans la spec 1.1. Dans FF2 c’était 2 max, mais dans IE8 c’est 6 je crois. Pour les navigateurs actuel, j’en ai aucune idée… Je crois que Firefox est à 15+ au moins. Ce qu’il faut savoir c’est que cette limite existe par domaine. Donc pendant un temps, la meilleure solution, c’était de répartir tes resources sur différents domaines. Je ne sais pas si cette astuce est toujours utile car les navigateurs ont mis la limite assez haute.

@e-t172: l’attribut defer (et async) sont arrivés dans webkit le mois dernier. Je ne pense pas que cette solution soit envisageable à l’heure actuelle malgré le fait qu’elle soit exactement ce que recherche max. Voir le trés bon article de Peter Beverloo :http://peter.sh/experiments/asynchronous-and-deferred-javascript-execution-explained/

par xandar
29 octobre 2010 à 10:57

Article (et commentaires) très intéressant. Merci :)

par Shtong
29 octobre 2010 à 12:12

Haha, c’est clair que je t’aurais fait des remarques avec du code inline dans les pages de la vekat :p
Pour moi la règle c’est : tous les CSS dans la balise head, tous les script en fin de fichier.

Sinon pour la limite de téléchargement, cette histoire vient de la RFC2616 (HTTP 1.1) chapitre 8.1.4 §6 :

Clients that use persistent connections SHOULD limit the number of
simultaneous connections that they maintain to a given server. A
single-user client SHOULD NOT maintain more than 2 connections with
any server or proxy.
A proxy SHOULD use up to 2*N connections to
another server or proxy, where N is the number of simultaneously
active users. These guidelines are intended to improve HTTP response
times and avoid congestion.

par e-t172
29 octobre 2010 à 14:18

« @e-t172: l’attribut defer (et async) sont arrivés dans webkit le mois dernier. Je ne pense pas que cette solution soit envisageable à l’heure actuelle malgré le fait qu’elle soit exactement ce que recherche max. »

Hein ? Ça m’étonne : l’attribut defer existe depuis plus de 10 ans ! (HTML 4.01)

par jye
29 octobre 2010 à 14:37

Effectivement désolé, j’ai tellement le nez dans la spec HTML5 que je pensais que tu y faisais reference: http://www.whatwg.org/specs/web-apps/current-work/multipage/scripting-1.html#script

L’ancien defer n’est de toute façon pas supporté par tous les navigateurs il me semble.

par Sylario
29 octobre 2010 à 17:34

Est il prévu dans le HTML 5 (6-7 8…) d’inclure une bibliothéque javascript de façon a proposer du code courant en standard dans chaque navigateur? A vous entendre on dirais que ce boulot est fait par JQuery.

29 octobre 2010 à 18:35

Très instructif, merci.

par xan
29 octobre 2010 à 20:04

Un dev de Yahoo a aussi publié un article récemment sur le pourcentage d’utilisateurs avec le Javascript désactivé, par ici.

Pour le reste, rien de vraiment neuf, charger le contenu avant le JS est devenu incontournable depuis un bail.

par __MaX__
30 octobre 2010 à 10:32

J’allais rebondir sur le defer, je ferais le test pour confirmer comment se comportent les threads, mais c’est uniquement un décalage d’exécution a priori.

Concernant les framework Sylario, le langage html et le javascript sont bien distincts. Je n’ai pas encore vu d’allusions laissant suggérer que les développeurs de navigateurs allaient inclure nativement des librairies de javascript dans leur navigateurs.
Mais dans le fond c’est une excellente idée. On pourrait même imaginer un plugin firefox ou chrome qui irait en permanence vérifier les dernières librairies js existantes (jquery, mootools & co) puis les téléchargeraient en arrière plan dans une cache locale et les exploiterait plutôt que les liens distants utilisés a chaque fois sur les différents sites.

Et oui, aujourd’hui ces librairies sont une grandes partie du fonctionnement d’un site. Par contre les sites proprement codés qui gèrent la navigation avec ou sans javascript c’est plutôt rare.

Tout ça pour rebondir sur ce que dit xan, je trouve encore assez restreint le pourcentage de gens qui désactivent le js. La somme de boulot pour gérer les deux cas de figure, c’est assez monstrueux… et aujourd’hui je considère que quelqu’un qui vient surfer sur un site “nouvelle génération” avec le js désactivé… c’est un peu comme un mec qui viendrait sur le circuit de monaco avec une lada et demanderait d’avoir les mêmes perfs qu’une F1.

Effectivement, charger le contenu avant le JS devient important depuis deux ans, mais franchement tu connais beaucoup de sites qui le font vraiment bien ? Tu prends des très grosses pointures comme materiel.net, rueducommerce, ldlc… ils ont tous tout dans les header.

Et merci jye pour l’explication, ça m’éclaire désormais un peu mieux sur le pourquoi de ce bloquage des js.

30 octobre 2010 à 11:00

Coder un site fonctionnel pour les gens ayant désactivé le JS c’est un perte de temps phénoménale. Mon point de vue, et celui de ma boite, c’est de rendre le site consultable, mais pas fonctionnel. Un joli noscript affiche en rouge bien visible toutes les fonctionnalités qui sont désactivées du fait de l’absence de JS.

par __MaX__
30 octobre 2010 à 11:14

Huhu, c’est pas ce que j’appelle fonctionnel. Pour moi fonctionnel c’est rendre par exemple tout l’ajax en post normal (comme la tribune de wefrag).

30 octobre 2010 à 11:22

Ah oui mais c’est ce que je dis, le site n’est PAS fonctionnel avec le JS désactivé, il est consultable.

par __MaX__
30 octobre 2010 à 12:43

Vu comme ça, effectivement le travail est moindre ;)

Commenter

Si vous avez un compte sur WeFrag, connectez-vous pour publier un commentaire.

Tags autorisés : <a href="" title="">...</a>,<b>...</b>,<blockquote cite="">...</blockquote>,<code>...</code>,<i>...</i>.