Optimisez le chargement 
de vos fichiers JavaScript

3mn de lecture

Depuis de nombreuses années, la "bonne pratique" afin de charger un fichier JavaScript est d'ajouter les balises <script> juste avant la fermeture de la balise <body>. Cette méthode est utilisée pour éviter que le téléchargement et l'analyse de vos scripts JavaScript bloque le rendu du navigateur, affichant une page blanche à l'utilisateur.

Malheureusement, cette technique est toujours enseignée et conseillée par certains alors qu'elle n'est plus d'actualité. Dans cet article, vous allez comprendre comment le chargement du JavaScript opère et comment l'optimiser au mieux selon votre besoin.

Chargement du JavaScript dans le navigateur

Afin de parler du chargement du JavaScript, il est important de comprendre comment le navigateur fonctionne après qu'il ait reçu une page HTML.

Sans rentrer dans des détails techniques, celui-ci va lire le fichier de haut en bas. Lorsqu'il rencontre une ressource externe, comme un fichier CSS, une image ou un fichier JavaScript, il va télécharger cette ressource et l'analyser.

Contrairement aux CSS ou aux images, le chargement d'un fichier JavaScript est bloquant. Le navigateur arrête toute analyse de l'HTML pour télécharger le fichier JavaScript et l'exécuter. La raison de ce comportement est que le script en question pourrait modifier le DOM (Document Object Model) ainsi que le CSSOM (CSS Object Model).

On remarque qu'avec ce fonctionnement, nous bloquons pendant un certains temps le rendu, d'où la solution de mettre les balises <script> avant la fin du <body>.

Chargement en utilisant l'attribut async

Fort heureusement, depuis l'arrivée de l'HTML 5, il est possible d'utiliser différents attributs sur la balise <script> afin de changer le fonctionement de celle-ci.

L'attribut async informe le navigateur de charger le script en asynchrone.

<script src="https://romainlanz.com/example.js" async></script>

Contrairement au fonctionnement de base, le téléchargement du script s'effectuera dans un autre thread, qui ne bloquera pas le rendu. En revanche, l'exécution de celui-ci bloquera toujours le rendu.

L'exécution n'a pas d'ordre précis. Le premier script entièrement chargé sera le premier script exécuté. Il n'attend également pas sur l'analyse de l'HTML ou quelconque événement du navigateur (comme DOMContentLoaded).

Cette méthode est utilisée pour charger des scripts externes qui n'ont aucune référence avec vos propres scripts, on peut citer comme exemple un script de tracking.

<script src="https://google-analytics.com/analytics.js" async></script>

Chargement en utilisant l'attribut defer

Un autre attribut utilisé pour modifier le chargement est defer.

Cet attribut va permettre au navigateur de charger le script en asynchrone et de l'exécuter après la fin de l'analyse du DOM.

<script src="https://romainlanz.com/example.js" defer></script>

Le chargement du fichier peut donc s'effectuer pendant l'analyse du DOM sans pour autant bloquer le rendu, vu qu'il sera seulement exécuté une fois l'analyse terminée.

Le fonctionnement de cet attribut est assez similaire à la technique de mettre les balises <script> avant la fin du <body>, mais il a l'avantage de télécharger en amont le fichier en parallèle du rendu, ce qui le rend plus performant.

Contrairement à async, l'attribut defer respecte l'ordre de saisie des scripts.

<script src="https://romainlanz.com/foo.js" defer></script>
<script src="https://romainlanz.com/bar.js" defer></script>

Dans cet exemple, le script foo.js sera exécuté avant le script bar.js. On peut également noter que l'événement DOMContentLoaded sera lancé qu'une fois bar.js chargé.

Vu que l'attribut defer attend la fin du chargement du DOM, tout script n'ayant pas cet attribut (hormis async) sera chargé auparavant.

Vous pouvez le tester par vous-même en allant sur ce lien: https://codepen.io/romainlanz/pen/wvKjRMO

Chargement en utilisant du JavaScript

Certains de vos scripts n'ont pas besoin d'être forcément charger directement avec votre page. Un exemple serait le lecteur de Youtube, celui-ci n'est pas utile tant que la personne ne clique pas sur la vidéo. On peut donner comme autre exemple un module de recherche. Un utilisateur naviguant sur votre site en aura pas l'utilité tant qu'il ne cliquera pas dans le champs de recherche.

Afin de soulager le travail des navigateurs, il est possible de charger en JavaScript ces scripts suite à un événement du DOM.

const searchElement = document.querySelector('input.search')

searchElement.addEventListener('focus', () => {
  const tag = document.createElement('script')
  tag.src = 'https://romainlanz.com/example.js'

  document.body.append(tag)
}, { once: true })

Le script ci-dessus va lancer le chargement du JavaScript ayant l'URL https://romainlanz.com/example.js seulement lorsque le champ search aura été focus.

Vous pouvez le tester par vous-même en allant sur ce lien: https://codepen.io/romainlanz/pen/gOazQRG

Conclusion

De nos jours, vos balises <script> ne devraient plus se trouver à la fin du <body>, mais dans le <head> en utilisant l'attribut defer.

Si un de vos scripts est lourd et spécifique à un élément, il devrait être chargé d'une manière différée en utilisant un événement du DOM.