Publié le 31 décembre 2013
JavaScript nous permet de modifier presque tous les aspects de la page : contenu, style et réponse aux interactions utilisateur. Toutefois, JavaScript peut aussi bloquer la construction DOM et retarder le rendu de la page. Afin d'optimiser les performances, rendre votre code JavaScript asynchrone et éliminer tout code JavaScript inutile du chemin critique du rendu.
Résumé
- JavaScript peut interroger et modifier le DOM et le CSSOM.
- Blocs d'exécution JavaScript sur le CSSOM
- JavaScript bloque la création du DOM, sauf si elle est explicitement déclarée comme asynchrone.
JavaScript est un langage dynamique qui s'exécute dans un navigateur et nous permet de modifier presque tous les aspects du comportement de la page : nous pouvons modifier le contenu en ajoutant et en supprimant des éléments de l'arborescence DOM, nous pouvons modifier les propriétés CSSOM de chaque élément, nous pouvons gérer les entrées utilisateur, et bien plus encore. Pour illustrer cela, observez ce qui se passe lorsque l'invite "Hello World" précédente est modifié pour ajouter un script inline court:
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width,initial-scale=1" />
<link href="style.css" rel="stylesheet" />
<title>Critical Path: Script</title>
</head>
<body>
<p>Hello <span>web performance</span> students!</p>
<div><img src="awesome-photo.jpg" /></div>
<script>
var span = document.getElementsByTagName('span')[0];
span.textContent = 'interactive'; // change DOM text content
span.style.display = 'inline'; // change CSSOM property
// create a new element, style it, and append it to the DOM
var loadTime = document.createElement('div');
loadTime.textContent = 'You loaded this page on: ' + new Date();
loadTime.style.color = 'blue';
document.body.appendChild(loadTime);
</script>
</body>
</html>
JavaScript nous permet d'accéder au DOM et d'extraire la référence au nœud de span masqué. Le nœud n'est peut-être pas visible dans l'arborescence de rendu, mais il est toujours présent dans le DOM. Ensuite, une fois la référence obtenue, nous pouvons modifier son texte (via .textContent) et même remplacer la propriété de style d'affichage calculée "none" par "none". à « inline ». La page affiche alors le message "Hello interactive student!"
JavaScript nous permet également de créer, de styliser, d'ajouter et de supprimer de nouveaux éléments dans le DOM. Techniquement, l'intégralité de notre page pourrait n'être qu'un seul grand fichier JavaScript qui crée et stylise les éléments un par un. Bien que cela fonctionne, en pratique, il est beaucoup plus simple d'utiliser HTML et CSS. Dans la deuxième partie de notre fonction JavaScript, nous allons créer un élément div, définir son contenu textuel, lui appliquer un style et l'ajouter au corps.
Nous avons ainsi modifié le contenu et le style CSS d'un nœud DOM existant, et ajouté un nouveau nœud au document. Notre page ne remportera pas de prix de conception, mais elle illustre la puissance et la flexibilité de JavaScript.
Cependant, bien que JavaScript offre de nombreuses fonctionnalités, il crée de nombreuses limitations supplémentaires sur la façon et le moment où la page est affichée.
Tout d'abord, notez que dans l'exemple précédent, notre script intégré se trouve dans la partie inférieure de la page. Pourquoi ? Vous devriez essayer vous-même, mais si nous déplaçons le script au-dessus de l'élément <span>
, vous remarquerez qu'il échoue et qu'il ne trouve aucune référence à aucun élément <span>
du document. Autrement dit, getElementsByTagName('span')
renvoie null
. Cela démontre une propriété importante : notre script est exécuté au point exact où il est inséré dans le document. Lorsque l'analyseur HTML rencontre un tag de script, il suspend son processus de construction du DOM et donne le contrôle au moteur JavaScript. Une fois l'exécution du moteur JavaScript terminée, le navigateur reprend là où il s'était arrêté et reprend la construction du DOM.
En d'autres termes, notre bloc de script ne peut trouver aucun élément plus loin sur la page, car ils n'ont pas encore été traités. Autrement dit, en d'autres termes, l'exécution de notre script intégré bloque la construction DOM, ce qui retarde également l'affichage initial.
Une autre propriété subtile de l'introduction de scripts sur notre page est qu'ils peuvent lire et modifier non seulement le DOM, mais aussi les propriétés CSSOM. En fait, c'est exactement ce que nous faisons dans notre exemple lorsque nous remplaçons la propriété d'affichage de l'élément span de "none" à "inline". Quelles conséquences cela a-t-il ? Nous avons maintenant une condition de concurrence.
Que se passe-t-il si le navigateur n'a pas terminé de télécharger et de créer le CSSOM au moment où nous voulons exécuter notre script ? La réponse n'est pas très bonne pour les performances : le navigateur retarde l'exécution du script et la création du DOM jusqu'à ce qu'il ait terminé de télécharger et de créer le CSSOM.
En résumé, JavaScript introduit de nombreuses nouvelles dépendances entre le DOM, le CSSOM et l'exécution JavaScript. Cela peut entraîner des retards importants dans le traitement et l'affichage de la page à l'écran :
- L'emplacement du script dans le document est important.
- Lorsque le navigateur rencontre une balise de script, la création du DOM est suspendue jusqu'à la fin de l'exécution du script.
- JavaScript peut interroger et modifier le DOM et le CSSOM.
- L'exécution de JavaScript est mise en pause jusqu'à ce que le CSSOM soit prêt.
Dans une large mesure, "optimiser le chemin critique du rendu" fait référence à la compréhension et à l'optimisation du graphique des dépendances entre HTML, CSS et JavaScript.
Blocage de l'analyseur et code JavaScript asynchrone
Par défaut, l'exécution JavaScript est "bloquante pour l'analyseur" : lorsque le navigateur rencontre un script dans le document, il doit suspendre la création du DOM, céder le contrôle à l'environnement d'exécution JavaScript et laisser le script s'exécuter avant de poursuivre la création du DOM. Nous avons vu cela en action avec un script intégré dans notre exemple précédent. En fait, les scripts intégrés sont toujours bloquants pour l'analyseur, sauf si vous écrivez du code supplémentaire pour différer leur exécution.
Qu'en est-il des scripts inclus à l'aide d'une balise de script ? Prenons l'exemple précédent et extrayons le code dans un fichier distinct :
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width,initial-scale=1" />
<link href="style.css" rel="stylesheet" />
<title>Critical Path: Script External</title>
</head>
<body>
<p>Hello <span>web performance</span> students!</p>
<div><img src="awesome-photo.jpg" /></div>
<script src="app.js"></script>
</body>
</html>
app.js
var span = document.getElementsByTagName('span')[0];
span.textContent = 'interactive'; // change DOM text content
span.style.display = 'inline'; // change CSSOM property
// create a new element, style it, and append it to the DOM
var loadTime = document.createElement('div');
loadTime.textContent = 'You loaded this page on: ' + new Date();
loadTime.style.color = 'blue';
document.body.appendChild(loadTime);
Si nous utilisons un <script> ou un extrait de code JavaScript intégré, s’attendent à ce que les deux se comportent de la même manière. Dans les deux cas, le navigateur met en pause et exécute le script avant de pouvoir traiter le reste du document. Toutefois, dans le cas d'un fichier JavaScript externe, le navigateur doit s'arrêter pour attendre que le script soit extrait du disque, du cache ou d'un serveur distant, ce qui peut ajouter des dizaines à des milliers de millisecondes de retard au chemin de rendu critique.
Par défaut, tout JavaScript bloque l'analyseur. Comme le navigateur ne sait pas ce que le script prévoit de faire sur la page, il part du pire et bloque l'analyseur. Un signal indiquant au navigateur que le script n'a pas besoin d'être exécuté au point exact où il est référencé permet au navigateur de continuer à construire le DOM et de laisser le script s'exécuter lorsqu'il est prêt, par exemple, après l'extraction du fichier à partir du cache ou d'un serveur distant.
Pour ce faire, l'attribut async
est ajouté à l'élément <script>
:
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width,initial-scale=1" />
<link href="style.css" rel="stylesheet" />
<title>Critical Path: Script Async</title>
</head>
<body>
<p>Hello <span>web performance</span> students!</p>
<div><img src="awesome-photo.jpg" /></div>
<script src="app.js" async></script>
</body>
</html>
L'ajout du mot clé "async" à la balise de script indique au navigateur de ne pas bloquer la création du DOM pendant qu'il attend que le script soit disponible, ce qui peut considérablement améliorer les performances.