Optimisation du démarrage de JavaScript

Lorsque nous créons des sites qui dépendent davantage de JavaScript, nous payons parfois pour ce que nous envoyons d'une manière que nous ne pouvons pas toujours voir facilement. Dans cet article, nous allons vous expliquer pourquoi un peu de discipline peut vous aider si vous souhaitez que votre site se charge et soit interactif rapidement sur les appareils mobiles. Fournir moins de code JavaScript peut signifier moins de temps de transmission réseau, moins de temps passé à décompresser le code, et moins de 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 pensent au coût de téléchargement et d'exécution. L'envoi d'un plus grand nombre d'octets de JavaScript sur le réseau prend plus de temps si la connexion de l'utilisateur est lente.

Lorsqu'un navigateur demande une ressource, celle-ci doit être récupérée, puis décompressée. Dans le cas de ressources telles que JavaScript, elles doivent être analysées et compilées avant l'exécution.

Cela peut poser problème, car le type de connexion réseau effectif d'un utilisateur n'est pas forcément 3G, 4G ou Wi-Fi. Vous pouvez être connecté au Wi-Fi d'un café, mais aussi à un point d'accès mobile avec un débit 2G.

Vous pouvez réduire le coût de transfert réseau de JavaScript de différentes manières :

  • Envoi du code dont l'utilisateur a besoin uniquement.
    • Utilisez la division du code pour séparer le code JavaScript critique du code non critique. Les empaqueteurs de modules comme webpack sont compatibles avec la division du code.
    • Chargement différé du code non critique.
  • Minimisation
  • Compression
    • Utilisez au moins gzip pour compresser les ressources textuelles.
    • Envisagez d'utiliser Brotli ~q11. Brotli surpasse gzip en termes de taux de compression. Il a permis à CertSimple de réduire de 17 % la taille des octets JS compressés et à LinkedIn de réduire de 4 % ses temps de chargement.
  • Supprimer le code inutilisé
  • Mise en cache du code pour minimiser les trajets 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 des scripts (max-age) et fournissez des jetons de validation (ETag) pour éviter de transférer des octets inchangés.
    • La mise en cache des service workers peut rendre votre application résiliente au réseau et vous donner un accès rapide à 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 filename hashing (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 nécessaire à un moteur JS pour analyser/compiler ce code. Dans les outils pour les développeurs Chrome, l'analyse et la compilation font partie du temps de "scripting" jaune dans le panneau "Performances".

ALT_TEXT_HERE

Les onglets "Bottom-Up" (Ascendant) et "Call Tree" (Arborescence des appels) indiquent les temps d'analyse/de compilation exacts :

ALT_TEXT_HERE
Panneau "Performances" des outils pour les développeurs Chrome > Bottom-Up. Lorsque les statistiques d'appel d'exécution de V8 sont activées, nous pouvons voir le temps passé dans des phases telles que l'analyse et la compilation.

Mais pourquoi est-ce important ?

ALT_TEXT_HERE

Si l'analyse/la compilation du code prend beaucoup de temps, cela peut retarder considérablement le moment où un utilisateur peut interagir avec votre site. Plus vous envoyez de code JavaScript, plus l'analyse et la compilation prendront du temps avant que votre site ne devienne interactif.

Octet par octet, le traitement du JavaScript est plus coûteux pour le navigateur que celui d'une image ou d'une 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 un matériel mobile moyen, JS est plus susceptible d'avoir un impact négatif sur l'interactivité d'une page.

ALT_TEXT_HERE
Les octets JavaScript et les octets d'image ont des coûts très différents. En général, les images ne bloquent pas le thread principal et n'empêchent pas les interfaces de devenir interactives pendant leur décodage et leur pixellisation. Toutefois, le code JS peut retarder l'interactivité en raison des coûts d'analyse, de compilation et d'exécution.

Lorsque nous parlons de lenteur d'analyse et de compilation, le contexte est important : nous parlons ici de téléphones mobiles moyens. Les utilisateurs moyens peuvent avoir des téléphones équipés de processeurs et de GPU lents, sans cache L2/L3, et qui peuvent même être limités en mémoire.

Les capacités du réseau et celles de l'appareil ne correspondent pas toujours. Un utilisateur disposant d'une excellente connexion Fiber n'a pas nécessairement le meilleur processeur pour analyser et évaluer le code JavaScript envoyé à son appareil. L'inverse est également vrai : 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/de compilation du code est deux à cinq fois plus long sur les téléphones moyens que sur les téléphones les plus rapides du marché.

ALT_TEXT_HERE
Ce graphique met en évidence les temps d'analyse d'un bundle JavaScript de 1 Mo (environ 250 Ko compressé au format gzip) sur des ordinateurs et des appareils mobiles de différentes classes. Lorsque vous examinez le coût de l'analyse, vous devez prendre en compte les chiffres décompressés.Par exemple, un fichier JS compressé de 250 Ko est décompressé en un code d'environ 1 Mo.

Qu'en est-il d'un site réel, comme CNN.com ?

Sur l'iPhone 8 haut de gamme, l'analyse et la compilation du code JS de CNN ne prennent que 4 secondes environ, contre 13 secondes environ pour un téléphone moyen (Moto G4). Cela peut avoir un impact significatif sur la rapidité avec laquelle un utilisateur peut interagir pleinement avec ce site.

ALT_TEXT_HERE
Ci-dessus, nous voyons les temps d'analyse comparant les performances de la puce A11 Bionic d'Apple à celles du Snapdragon 617 sur un matériel Android plus moyen.

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 dans votre poche. Toutefois, le contexte est important : optimisez votre site en fonction de l'appareil et des conditions réseau de vos utilisateurs.

ALT_TEXT_HERE
Google Analytics peut vous fournir des informations sur les classes d'appareils mobiles que vos utilisateurs réels utilisent pour accéder à votre site. Cela peut vous aider à comprendre les contraintes réelles de CPU/GPU auxquelles ils sont confrontés.

Envoyons-nous vraiment trop de JavaScript ? Euh, peut-être :)

En utilisant HTTP Archive (les 500 000 sites les plus populaires) pour analyser l'état de JavaScript sur mobile, nous pouvons constater que 50 % des sites mettent plus de 14 secondes à devenir interactifs. Ces sites passent jusqu'à quatre secondes à analyser et compiler le code JS.

ALT_TEXT_HERE

Si l'on tient compte du temps nécessaire pour récupérer et traiter le code JS 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 à être utilisées. Nous pouvons certainement faire mieux ici.

Supprimer le code JavaScript non critique de vos pages peut réduire les temps de transmission, l'analyse et la compilation gourmandes en ressources du processeur, ainsi que la surcharge de mémoire potentielle. 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 à avoir un coût. L'exécution JavaScript (exécution du code 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.

ALT_TEXT_HERE

Si l'exécution du script prend plus de 50 ms, le délai avant interactivité est retardé de l'intégralité du temps nécessaire au téléchargement, à la compilation et à l'exécution du code JS. – Alex Russell

Pour résoudre ce problème, JavaScript peut être divisé en petits fragments afin d'éviter de bloquer le thread principal. Déterminez si vous pouvez réduire la quantité de travail effectuée pendant l'exécution.

Autres frais

JavaScript peut avoir un impact sur les performances de la page d'autres manières :

  • Mémoire. Les pages peuvent sembler saccadées ou s'interrompre 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 de JS est mise en pause. Par conséquent, un navigateur qui récupère fréquemment de la mémoire peut mettre en pause l'exécution plus souvent que nous le souhaiterions. Évitez les fuites de mémoire et les pauses fréquentes de récupération de mémoire pour éviter les à-coups sur les pages.
  • Pendant l'exécution, le code JavaScript de longue durée peut bloquer le thread principal, ce qui entraîne l'absence de réponse des pages. La division du travail en plus petits morceaux (à l'aide de requestAnimationFrame() ou de requestIdleCallback() pour la planification) peut minimiser les problèmes de réactivité, ce qui peut contribuer à améliorer l'Interaction to Next Paint (INP).

Modèles pour réduire le coût de diffusion de JavaScript

Lorsque vous essayez de maintenir des temps d'analyse/de compilation et de transmission réseau lents pour JavaScript, certains modèles peuvent vous aider, comme le chunking basé sur les routes ou PRPL.

PRPL

PRPL (Push, Render, Pre-cache, Lazy-load) est un modèle qui optimise l'interactivité grâce à une division agressive du code et à une mise en cache préalable :

ALT_TEXT_HERE

Voyons l'impact que cela peut avoir.

Nous analysons le temps de chargement des sites mobiles populaires et des progressive web apps à l'aide des statistiques d'appel d'exécution de V8. Comme nous pouvons le voir, le temps d'analyse (indiqué en orange) représente une part importante du temps passé par un grand nombre de ces sites :

ALT_TEXT_HERE

Wego, un site qui utilise PRPL, parvient à maintenir un temps d'analyse faible pour ses routes, ce qui permet d'obtenir une interactivité très rapidement. De nombreux autres sites ci-dessus ont adopté la division du code et les budgets de performances pour essayer de réduire leurs coûts JS.

Bootstrapping progressif

De nombreux sites optimisent la visibilité du contenu au détriment de l'interactivité. Pour obtenir un premier rendu rapide lorsque vous disposez de bundles JavaScript volumineux, les développeurs utilisent parfois le rendu côté serveur, puis le "mettent à niveau" pour associer des gestionnaires d'événements lorsque le code JavaScript est enfin récupéré.

Attention, cela a un coût. 1) Vous envoyez généralement une réponse HTML plus volumineuse qui peut retarder l'interactivité, 2) vous pouvez laisser l'utilisateur dans une situation étrange où la moitié de l'expérience ne peut pas être interactive tant que le traitement JavaScript n'est pas terminé.

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 d'autres ressources arrivent, l'application peut charger de manière différée et débloquer d'autres fonctionnalités.

ALT_TEXT_HERE
Progressive Bootstrapping par Paul Lewis

Le chargement du code proportionnel à ce qui est affiché est le Saint Graal. PRPL et l'amorçage progressif sont des modèles qui peuvent vous aider à y parvenir.

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 maintenir ces valeurs à un niveau faible.

Les équipes ont réussi à adopter des budgets de performances stricts pour maintenir des temps de transmission et d'analyse/compilation JavaScript faibles. Consultez l'article d'Alex Russell intitulé Can You Afford It?: Real-world Web Performance Budgets" (en anglais) pour obtenir des conseils sur les budgets pour mobile.

ALT_TEXT_HERE
Il est utile de réfléchir à la marge de manœuvre que les décisions architecturales que nous prenons peuvent nous laisser pour la logique de l'application.

Si vous créez un site ciblant les appareils mobiles, faites de votre mieux pour développer sur du matériel représentatif, maintenez des temps d'analyse/de compilation JavaScript faibles et adoptez un budget de performances pour vous assurer que votre équipe peut garder un œil sur ses coûts JavaScript.

En savoir plus