À mesure que nous créons des sites de plus en plus dépendants de JavaScript, nous payons parfois pour ce que nous envoyons de manières que nous ne pouvons pas toujours voir facilement. Dans cet article, nous allons voir pourquoi un peu de discipline peut vous aider si vous souhaitez que votre site se charge et soit interactif rapidement sur les appareils mobiles. Transmettre moins de code JavaScript peut réduire le temps de transmission réseau, le temps consacré à la décompression du code et le temps d'analyse et de compilation de ce code JavaScript.
Réseau
Lorsque la plupart des développeurs pensent au coût de JavaScript, ils le font en termes de coût de téléchargement et d'exécution. Plus la connexion d'un utilisateur est lente, plus l'envoi d'octets de code JavaScript sur le réseau prend du temps.
Cela peut poser problème, car le type de connexion réseau effectif d'un utilisateur peut ne pas être la 3G, la 4G ou le Wi-Fi. Vous pouvez être connecté au Wi-Fi d'un café, mais à un point d'accès mobile avec des débits 2G.
Vous pouvez réduire les coûts de transfert réseau du code JavaScript en procédant comme suit:
- Envoyer uniquement le code dont l'utilisateur a besoin
- Utilisez le fractionnement du code pour diviser votre code JavaScript en éléments essentiels et non essentiels. Les outils de regroupement de modules tels que webpack sont compatibles avec le fractionnement du code.
- Chargement différé du code non critique.
- Minimisation
- Utilisez UglifyJS pour minimiser le code ES5.
- Utilisez babel-minify ou uglify-es pour réduire ES2015 et versions ultérieures.
- Compression
- Utilisez au minimum gzip pour compresser les ressources textuelles.
- Envisagez d'utiliser Brotli ~q11. Brotli est plus performant que gzip en termes de taux de compression. Cela a permis à CertSimple de réduire la taille des octets JS compressés de 17% et à LinkedIn de réduire ses temps de chargement de 4%.
- Supprimez le code inutilisé.
- Identifiez les opportunités de code pouvant être supprimé ou chargé de manière paresseuse avec la couverture du code DevTools.
- Utilisez babel-preset-env et browserlist pour éviter de transcompiler des fonctionnalités déjà présentes dans les navigateurs modernes. Les développeurs expérimentés peuvent constater qu'une analyse minutieuse de leurs bundles webpack permet d'identifier les opportunités de réduire les dépendances inutiles.
- Pour supprimer du code, consultez le tree-shaking, les optimisations avancées du compilateur Closure et les plug-ins de suppression des éléments inutiles de la bibliothèque, comme lodash-babel-plugin ou le ContextReplacementPlugin de webpack pour les bibliothèques telles que Moment.js.
- Mettre en cache le code pour réduire le nombre de requêtes réseau
- Utilisez la mise en cache HTTP pour vous assurer que les navigateurs mettent en cache les réponses de manière efficace. Déterminez les durées de vie optimales pour les scripts (max-age) et les jetons de validation de l'offre (ETag) afin d'éviter de transférer des octets inchangés.
- La mise en cache du service worker peut rendre le réseau de votre application résilient et vous donner un accès anticipé à des fonctionnalités telles que le cache de code de V8.
- Utilisez la mise en cache à long terme pour éviter d'avoir à récupérer à nouveau des ressources qui n'ont pas changé. Si vous utilisez Webpack, consultez la section hachage de nom de fichier.
Analyser/Compiler
Une fois téléchargé, l'un des coûts les plus lourds de JavaScript est le temps qu'un moteur JS met à analyser/compiler ce code. Dans Chrome DevTools, l'analyse et la compilation font partie du temps "Scripting" jaune dans le panneau "Performances".
Les onglets "Bottom-Up" (De bas en haut) et "Call Tree" (Arborescence des appels) affichent les délais d'analyse/compilation exacts:

Mais pourquoi est-ce important ?
Passer beaucoup de temps à analyser/compiler du code peut retarder considérablement le temps d'interaction d'un utilisateur avec votre site. Plus vous envoyez de code JavaScript, plus il faudra de temps pour l'analyser et le compiler avant que votre site ne soit interactif.
Pour chaque octet, le traitement de JavaScript par le navigateur est plus coûteux que celui de l'image ou de la police Web de taille équivalente. – Tom Dale
Par rapport à JavaScript, le traitement d'images de taille équivalente implique de nombreux coûts (elles doivent toujours être décodées), mais sur les appareils mobiles moyens, JS est plus susceptible d'avoir un impact négatif sur l'interactivité d'une page.

Lorsque nous parlons de la lenteur de l'analyse et de la compilation, le contexte est important. Nous parlons ici de téléphones mobiles moyens. Les utilisateurs moyens peuvent avoir des téléphones avec des processeurs et des GPU lents, sans cache L2/L3 et qui peuvent même être limités en mémoire.
Les fonctionnalités du réseau et celles de l'appareil ne correspondent pas toujours. Un utilisateur disposant d'une connexion Fiber exceptionnelle n'a pas nécessairement le meilleur processeur pour analyser et évaluer le code JavaScript envoyé à son appareil. Cela est également vrai dans le sens inverse : une connexion réseau terrible, mais un processeur ultra-rapide. — Kristofer Baxter, LinkedIn
Vous trouverez ci-dessous le coût de l'analyse d'environ 1 Mo de code JavaScript décompressé (simple) sur du matériel bas de gamme et haut de gamme. Le temps d'analyse/compilation du code varie de 2 à 5 fois entre les téléphones les plus rapides du marché et les téléphones moyens.

Qu'en est-il d'un site réel, comme CNN.com ?
Sur l'iPhone 8 haut de gamme, il ne faut que 4 secondes pour analyser/compiler le code JavaScript de CNN, contre 13 secondes pour un téléphone moyen (Moto G4). Cela peut avoir un impact important sur la rapidité avec laquelle un utilisateur peut interagir pleinement avec ce site.

Cela souligne l'importance de tester sur du matériel moyen (comme le Moto G4) plutôt que sur le téléphone que vous avez peut-être dans votre poche. Le contexte est toutefois important: optimisez pour les conditions de l'appareil et du réseau de vos utilisateurs.

Envoyons-nous vraiment trop de code JavaScript ? Euh, peut-être :)
En utilisant HTTP Archive (environ 500 000 sites populaires) pour analyser l'état de JavaScript sur mobile, nous constatons que 50% des sites mettent plus de 14 secondes à devenir interactifs. Ces sites passent jusqu'à quatre secondes à analyser et compiler du code JavaScript.
Compte tenu du temps nécessaire pour extraire et traiter le code JavaScript et d'autres ressources, il n'est peut-être pas surprenant que les utilisateurs doivent attendre un certain temps avant de sentir que les pages sont prêtes à l'emploi. Nous pouvons certainement faire mieux.
Supprimer le code JavaScript non critique de vos pages peut réduire les temps de transmission, l'analyse et la compilation gourmande en processeur, ainsi que les coûts de mémoire potentiels. Cela permet également de rendre vos pages interactives plus rapidement.
Durée d'exécution
L'analyse et la compilation ne sont pas les seules opérations qui peuvent avoir un coût. L'exécution JavaScript (code d'exécution une fois analysé/compilé) est l'une des opérations qui doivent se produire sur le thread principal. Des temps d'exécution longs peuvent également retarder le moment où un utilisateur peut interagir avec votre site.
Si l'exécution du script dure plus de 50 ms, le délai d'interactivité est retardé par la durée complète nécessaire pour télécharger, compiler et exécuter le code JavaScript. – Alex Russell
Pour y remédier, il est préférable que JavaScript soit divisé en petits blocs afin d'éviter de bloquer le thread principal. Essayez de réduire la quantité de travail effectuée pendant l'exécution.
Autres frais
JavaScript peut également avoir un impact sur les performances des pages:
- Mémoire Les pages peuvent sembler saccadées ou s'arrêter fréquemment en raison de la récupération de mémoire. Lorsqu'un navigateur récupère de la mémoire, l'exécution JavaScript est mise en pause. Un navigateur qui effectue fréquemment une récupération de mémoire peut donc mettre en pause l'exécution plus fréquemment que nous le souhaiterions. Évitez les fuites de mémoire et les pauses de récupération de mémoire fréquentes pour éviter les à-coups sur les pages.
- Lors de l'exécution, le code JavaScript de longue durée peut bloquer le thread principal, ce qui entraîne des pages qui ne répondent pas. Décomposer le travail en petites parties (à l'aide de
requestAnimationFrame()
ourequestIdleCallback()
pour la planification) peut réduire les problèmes de réactivité, ce qui peut contribuer à améliorer l'Interaction to Next Paint (INP).
Modèles permettant de réduire les coûts de diffusion JavaScript
Lorsque vous essayez de ralentir les temps d'analyse/compilation et de transmission réseau pour JavaScript, certains modèles peuvent vous aider, comme le découpage basé sur les routes ou PRPL.
PRPL
PRPL (Push, Render, Pre-cache, Lazy-load) est un modèle qui optimise l'interactivité grâce à un fractionnement et un mise en cache du code agressifs:
Voyons l'impact qu'elle peut avoir.
Nous analysons le temps de chargement des sites mobiles populaires et des applications Web progressives à l'aide des statistiques d'appel d'exécution de V8. Comme vous pouvez le constater, la durée d'analyse (en orange) représente une part importante du temps passé par de nombreux sites:
Wego, un site qui utilise PRPL, parvient à maintenir un temps d'analyse faible pour ses itinéraires, ce qui le rend très interactif. De nombreux autres sites ci-dessus ont adopté le fractionnement du code et les budgets de performances pour essayer de réduire leurs coûts en JS.
Amorçage progressif
De nombreux sites optimisent la visibilité du contenu au détriment de l'interactivité. Pour obtenir une première peinture rapide lorsque vous disposez de grands groupes JavaScript, les développeurs utilisent parfois le rendu côté serveur, puis le "mettront à niveau" pour associer des gestionnaires d'événements lorsque le code JavaScript sera finalement extrait.
Attention, cela entraîne des coûts. Vous 1) envoyez généralement une réponse HTML plus importante, ce qui peut accroître l'interactivité, 2) pouvez laisser l'utilisateur dans une vallée étrange où la moitié de l'expérience ne peut pas être interactive tant que JavaScript n'a pas terminé le traitement.
Le bootstrapping progressif peut être une meilleure approche. Envoyez une page fonctionnelle minimale (composée uniquement du code HTML/JS/CSS nécessaire pour l'itinéraire actuel). À mesure que de nouvelles ressources arrivent, l'application peut charger de manière différée et déverrouiller d'autres fonctionnalités.

Le chargement de code proportionnel à ce qui est visible est le Saint Graal. PRPL et le démarrage progressif sont des modèles qui peuvent vous y aider.
Conclusions
La taille de transmission est essentielle pour les réseaux bas de gamme. Le temps d'analyse est important pour les appareils liés au processeur. Il est important de les maintenir à un niveau bas.
Les équipes ont réussi à adopter des budgets de performances stricts pour réduire les temps de transmission et d'analyse/compilation JavaScript. Consultez Can You Afford It? "Budgets de performances Web réels" pour obtenir des conseils sur les budgets pour les mobiles.

Si vous créez un site qui cible les appareils mobiles, faites de votre mieux pour développer sur du matériel représentatif, réduisez les temps d'analyse/compilation JavaScript et adoptez un budget de performances pour vous assurer que votre équipe peut surveiller ses coûts JavaScript.
En savoir plus
- Chrome Dev Summit 2017 : bonnes pratiques de chargement moderne
- Performances de démarrage JavaScript
- Résoudre la crise des performances Web – Nolan Lawson
- Pouvez-vous vous le permettre ? Budgets de performances réels — Alex Russell
- Évaluer les frameworks et les bibliothèques Web – Kristofer Baxter
- Résultats des tests de compression avec Brotli de Cloudflare (notez que la compression Brotli dynamique à une qualité plus élevée peut retarder l'affichage initial de la page. Évaluez donc attentivement. Vous voudrez probablement compresser de manière statique à la place.)
- Performance Futures — Sam Saccone