Dans les sites Web modernes, les scripts sont souvent âplus lourdsâ que le HTML: leur taille de tĂ©lĂ©chargement est plus grande et le temps de traitement est Ă©galement plus long.
Lorsque le navigateur charge le HTML et rencontre une balise <script>...</script>, il ne peut pas continuer Ă construire le DOM. Il doit exĂ©cuter le script de suite. Il en va de mĂȘme pour les scripts externes <script src ="..."></script>: le navigateur doit attendre le tĂ©lĂ©chargement du script, lâexĂ©cuter, puis traiter le reste de la page.
Cela conduit Ă deux problĂšmes importants:
- Les scripts ne peuvent pas voir les Ă©lĂ©ments DOM en dessous dâeux, ils ne peuvent donc pas ajouter de gestionnaires, etc.
- Sâil y a un script volumineux en haut de la page, il âbloque la pageâ. Les utilisateurs ne peuvent pas voir le contenu de la page tant quâil nâest pas tĂ©lĂ©chargĂ© et exĂ©cutĂ©:
<p>...content before script...</p>
<script src="https://javascript.info/article/script-async-defer/long.js?speed=1"></script>
<!-- Ceci n'est pas visible tant que le script n'est pas chargé -->
<p>...content after script...</p>
Il existe quelques solutions pour contourner cela. Par exemple, nous pouvons mettre un script en bas de page. Comme ça, il peut voir les Ă©lĂ©ments au-dessus, et cela ne bloque pas lâaffichage du contenu de la page:
<body>
...all content is above the script...
<script src="https://javascript.info/article/script-async-defer/long.js?speed=1"></script>
</body>
Mais cette solution est loin dâĂȘtre parfaite. Par exemple, le navigateur remarque le script (et peut commencer Ă le tĂ©lĂ©charger) uniquement aprĂšs avoir tĂ©lĂ©chargĂ© le document HTML complet. Pour les longs documents HTML, cela peut ĂȘtre un retard notable.
De telles choses sont invisibles pour les personnes utilisant des connexions trĂšs rapides, mais de nombreuses personnes dans le monde ont encore des vitesses Internet lentes et utilisent une connexion Internet mobile loin dâĂȘtre parfaite.
Heureusement, il y a deux attributs de <script> qui résolvent le problÚme pour nous: defer et async.
defer
Lâattribut defer indique au navigateur de ne pas attendre le script. Au lieu de cela, le navigateur continuera Ă traiter le HTML, Ă construire le DOM. Le script se charge âen arriĂšre-planâ, puis sâexĂ©cute lorsque le DOM est entiĂšrement construit.
Voici le mĂȘme exemple que ci-dessus, mais avec defer :
<p>...content before script...</p>
<script defer src="https://javascript.info/article/script-async-defer/long.js?speed=1"></script>
<!-- visible immédiatement -->
<p>...content after script...</p>
- Les scripts avec
deferne bloquent jamais la page. - Les scripts avec
defersâexĂ©cutent toujours lorsque le DOM est prĂȘt, mais avant lâĂ©vĂ©nementDOMContentLoaded.
Lâexemple suivant montre que:
<p>...content before scripts...</p>
<script>
document.addEventListener('DOMContentLoaded', () => alert("DOM ready after defer!")); // (2)
</script>
<script defer src="https://javascript.info/article/script-async-defer/long.js?speed=1"></script>
<p>...content after scripts...</p>
- Le contenu de la page sâaffiche immĂ©diatement.
DOMContentLoadedattend le script différé. Il ne se déclenche que lorsque le script(2)est téléchargé et exécuté.
Les scripts différés conservent leur ordre relatif, tout comme les scripts classiques.
Donc, si nous avons dâabord un long script, puis un plus petit, alors ce dernier attend.
<script defer src="https://javascript.info/article/script-async-defer/long.js"></script>
<script defer src="https://javascript.info/article/script-async-defer/small.js"></script>
Les navigateurs analysent la page Ă la recherche de scripts et les tĂ©lĂ©chargent en parallĂšle pour amĂ©liorer les performances. Ainsi, dans lâexemple ci-dessus, les deux scripts se tĂ©lĂ©chargent en parallĂšle. Le small.js se termine probablement en premier.
⊠Mais lâattribut defer, en plus de dire au navigateur âde ne pas bloquerâ, garantit que lâordre relatif est conservĂ©. Ainsi, mĂȘme si small.js se charge en premier, il attend et sâexĂ©cute toujours aprĂšs lâexĂ©cution de long.js.
Mais la spĂ©cification exige que les scripts sâexĂ©cutent dans lâordre des documents, donc elle attend que long.js sâexĂ©cute.
```smart header="L'attribut `defer` est uniquement pour les scripts externes"
L'attribut `defer` est ignoré si la balise `<script>` n'a pas de `src`.
async
- Le navigateur ne bloque pas les scripts
async(commedefer). - Dâautres scripts nâattendent pas les scripts
async, et les scriptsasyncne les attendent pas. DOMContentLoadedet les scripts asynchrones ne sâattendent pas :DOMContentLoadedpeut se produire Ă la fois avant un script asynchrone (si un script async termine le chargement une fois la page terminĂ©e)- ⊠ou aprĂšs un script async (si un script async est court ou Ă©tait dans le cache HTTP)
En dâautres termes, les scripts async se chargent en arriĂšre-plan et sâexĂ©cutent lorsquâils sont prĂȘts. Le DOM et les autres scripts ne les attendent pas, et ils nâattendent rien. Un script entiĂšrement indĂ©pendant qui sâexĂ©cute lorsquâil est chargĂ©. Aussi simple que cela puisse ĂȘtre, non ?
Donc, si nous avons plusieurs scripts async, ils peuvent sâexĂ©cuter dans nâimporte quel ordre. Premier chargĂ© â premier exĂ©cutĂ©:
<p>...content before scripts...</p>
<script>
document.addEventListener('DOMContentLoaded', () => alert("DOM ready!"));
</script>
<script async src="https://javascript.info/article/script-async-defer/long.js"></script>
<script async src="https://javascript.info/article/script-async-defer/small.js"></script>
<p>...content after scripts...</p>
- Le contenu de la page apparaßt immédiatement:
asyncne la bloque pas. DOMContentLoadedpeut arriver soit avant ou aprĂšsasync, aucune garantie ici.- Les scripts asynchrones nâattendent pas les uns les autres. Un script plus petit
small.jspasse en second, mais se charge probablement avantlong.js, donc sâexĂ©cute en premier. Câest ce quâon appelle une commande âload-firstâ.
Les scripts asynchrones sont parfaits lorsque nous intégrons un script tiers indépendant dans la page: compteurs, publicités, etc., car ils ne dépendent pas de nos scripts et nos scripts ne doivent pas les attendre:
<!-- Google Analytics est généralement ajouté comme ceci -->
<script async src="https://google-analytics.com/analytics.js"></script>
async est uniquement pour les scripts externesTout comme defer, lâattribut async est ignorĂ© si la balise <script> nâa pas de src.
Les scripts dynamiques
Il existe un autre moyen important dâajouter un script Ă la page.
Nous pouvons également ajouter un script dynamiquement en utilisant JavaScript:
let script = document.createElement('script');
script.src = "/article/script-async-defer/long.js";
document.body.append(script); // (*)
Le script commence Ă se charger dĂšs quâil est ajoutĂ© au document (*).
Les scripts dynamiques se comportent comme âasynchronesâ par dĂ©faut.
Câest-Ă -dire :
- Ils nâattendent rien, rien ne les attend.
- Le script qui se charge en premier â sâexĂ©cute en premier (âload-firstâ).
let script = document.createElement('script');
script.src = "/article/script-async-defer/long.js";
script.async = false;
document.body.append(script);
Par exemple, nous ajoutons ici deux scripts. Sans script.async=false, ils sâexĂ©cuteraient dans lâordre de chargement (le small.js probablement en premier). Mais avec, lâordre est âcomme dans le documentâ:
function loadScript(src) {
let script = document.createElement('script');
script.src = src;
script.async = false;
document.body.append(script);
}
// long.js s'exécute en premier à cause de async=false
loadScript("/article/script-async-defer/long.js");
loadScript("/article/script-async-defer/small.js");
Résumé
Async et defer ont un point commun: le tĂ©lĂ©chargement de tels scripts ne bloque pas le rendu des pages. Ainsi, lâutilisateur peut lire le contenu de la page et se familiariser immĂ©diatement avec la page.
Mais il existe également des différences essentielles entre eux:
| Lâordre | DOMContentLoaded |
|
|---|---|---|
async |
Load-first. Leur ordre dans le document nâa pas dâimportance â premier chargĂ©, premier exĂ©cutĂ© | Sans importance. Peut se charger et sâexĂ©cuter alors que le document nâa pas encore Ă©tĂ© entiĂšrement tĂ©lĂ©chargĂ©. Cela se produit si les scripts sont petits ou mis en cache et que le document est suffisamment long. |
defer |
Ordre de document (tel quâil apparaĂźt dans le document). | ExĂ©cute aprĂšs le chargement et lâanalyse du document (ils attendent si nĂ©cessaire), juste avant DOMContentLoaded. |
En pratique, defer est utilisĂ© pour les scripts qui ont besoin de tout le DOM et/ou leur ordre dâexĂ©cution relatif est important.
Et async est utilisĂ© pour des scripts indĂ©pendants, comme des compteurs ou des publicitĂ©s. Et leur ordre dâexĂ©cution relatif nâa pas dâimportance.
Veuillez noter que si vous utilisez defer ou async, lâutilisateur verra alors la page avant le chargement du script.
Lâutilisateur peut donc lire la page, mais certains composants graphiques ne sont probablement pas encore prĂȘts.
Il devrait y avoir des indications de âchargementâ aux bons endroits et les boutons dĂ©sactivĂ©s devraient sâafficher comme tels, afin que lâutilisateur puisse voir clairement ce qui est prĂȘt et ce qui ne lâest pas.
Commentaires
<code>, pour plusieurs lignes â enveloppez-les avec la balise<pre>, pour plus de 10 lignes - utilisez une sandbox (plnkr, jsbin, codepenâŠ)