351 votes

ordre de chargement et d'exécution des scripts

Il y a tellement de façons différentes d'inclure JavaScript dans une page html. Je connais les options suivantes :

  • code en ligne ou chargé depuis un URI externe
  • inclus dans la balise <head> ou <body> [ 1 , 2 ]
  • n'en ayant pas, defer ou async (seulement les scripts externes)
  • inclus dans une source statique ou ajouté dynamiquement par d'autres scripts (à différents états d'analyse, avec différentes méthodes)

Sans compter les scripts de navigation du disque dur, les javascript:URIs et les onEvent -attributs [ 3 ], il y a déjà 16 alternatives pour faire exécuter JS et je suis sûr que j'ai oublié quelque chose.

Je ne me préoccupe pas tellement de la rapidité du chargement (parallèle), je suis plus curieux de l'ordre d'exécution (qui peut dépendre de l'ordre de chargement et de l'ordre d'exécution). commande de documents ). Y a-t-il un bon (cross-browser) référence qui couvre vraiment tous les cas ? Par exemple http://www.websiteoptimization.com/speed/tweak/defer/ ne traite que 6 d'entre eux, et teste surtout les anciens navigateurs.

Comme je crains que ce ne soit pas le cas, voici ma question spécifique : J'ai quelques scripts de tête (externes) pour l'initialisation et le chargement des scripts. Ensuite, j'ai deux scripts statiques, en ligne, à la fin du corps. Le premier permet au chargeur scripts d'ajouter dynamiquement un autre élément scripts (référençant des js externes) au corps. Le second des scripts statiques, en ligne, veut utiliser les js du scripts externe ajouté. Peut-il compter sur le fait que l'autre a été exécuté (et pourquoi :-) ?

0 votes

Avez-vous regardé Chargement des scripts sans blocage par Steve Souders ? Il date un peu aujourd'hui, mais contient toujours des informations précieuses sur le comportement des navigateurs en fonction d'une technique de chargement de script spécifique.

449voto

jfriend00 Points 152127

Si vous n'êtes pas le chargement dynamique de scripts ou en les marquant comme reporter ou asynchrone, puis les scripts sont chargés dans l'ordre rencontrées dans la page. Il n'a pas d'importance si c'est un script externe ou un script en ligne - ils sont exécutés dans l'ordre où ils sont rencontrés dans la page. Inline scripts qui viennent après des scripts externes sont tenues jusqu'à ce que tous les scripts externes qui sont venus avant eux ont chargé et exécuté.

Async scripts (indépendamment de la façon dont ils sont spécifiés comme async) de charger et d'exécuter dans un ordre imprévisible. Le navigateur charge en parallèle et il est libre de les exécuter dans n'importe quel ordre qu'il veut.

Il n'y a pas d'ordre prévisible entre plusieurs async choses. Si on a besoin d'un ordre prévisible, alors il devrait être codée dans en vous inscrivant à la charge des notifications à partir de la async scripts manuellement et de séquençage javascript appels lorsque les choses sont chargés.

Lorsqu'une balise script est inséré dynamiquement, la façon dont l'ordre d'exécution se comporte dépendra du navigateur. Vous pouvez voir comment Firefox se comporte dans cet article de référence. En un mot, les versions récentes de Firefox par défaut une dynamique ajouté balise de script async, à moins que la balise de script a été définie autrement.

Voici une citation de cet article:

script insérer des scripts à exécuter de manière asynchrone dans IE et WebKit, mais de façon synchrone à l'Opéra et le pré-4.0 de Firefox.

La partie pertinente de la spec HTML5 (pour les nouveaux navigateurs compatibles) est ici. Il y a beaucoup écrit sur les async comportement. Évidemment, cette spécification ne s'applique pas pour les navigateurs plus anciens (ou mal-confirmant les navigateurs) qui est le comportement que vous aurait probablement pour tester à déterminer.

Une citation de la spec HTML5:

Alors, la première des options suivantes qui décrit la situation doivent être suivies:

Si l'élément possède un attribut src, et cet élément a un reporter attribut, et l'élément qui a été marqué comme "analyseur-insérés", et l'élément n'a pas un attribut async L'élément doit être ajouté à la fin de la liste des scripts à exécuter lorsque le document a terminé l'analyse associée avec le Document de l'analyseur que créé l'élément.

La tâche, la mise en réseau de la tâche source des lieux sur la tâche de la file d'attente une fois la récupération de l'algorithme a terminé devez définir l'élément "prête à être analyseur exécuté" pavillon. L'analyseur va gérer l'exécution du script.

Si l'élément possède un attribut src, et l'élément a été signalé "parser-insérés", et l'élément n'a pas un attribut async L'élément est dans l'attente de l'analyse de blocage de script du Document de l'analyseur qui a créé l'élément. (Il peut seulement être un de ces script par Document à la fois.)

La tâche, la mise en réseau de la tâche source des lieux sur la tâche de la file d'attente une fois la récupération de l'algorithme a terminé devez définir l'élément "prête à être analyseur exécuté" pavillon. L'analyseur va gérer l'exécution du script.

Si l'élément n'a pas un attribut src, et l'élément a été marqué comme "analyseur-insérés", et le Document de l'analyseur HTML ou Analyseur XML qui a créé l'élément script a une feuille de style qui est le blocage des scripts de L'élément est dans l'attente de l'analyse de blocage de script de le Document de l'analyseur qui a créé l'élément. (Il ne peut être un tel script par Document à la fois.)

Définissez l'élément "prête à être analyseur exécuté" pavillon. L'analyseur gérer l'exécution du script.

Si l'élément possède un attribut src, ne dispose pas d'un attribut async, et n'a pas la "force-async" drapeau de L'élément doit être ajouté à la fin de la liste des scripts qui s'exécutent dans l'ordre dès que possible associée avec le Document de l'élément script à l' le temps de la préparer un script algorithme a commencé.

La tâche, la mise en réseau de la tâche source des lieux sur la tâche de la file d'attente une fois la récupération de l'algorithme a terminé devez exécuter les étapes suivantes:

Si l'élément n'est pas le premier élément dans la liste des scripts qui va l'exécuter dans l'ordre dès que possible à laquelle il a été ajouté ci-dessus, puis marquer l'élément comme prêt à abandonner ces étapes sans l'exécution du script, encore.

Exécution: Exécuter le bloc de script correspondant à la première script élément dans cette liste de scripts qui s'exécutent dans l'ordre dès que possible.

Supprimer le premier élément de cette liste de scripts à exécuter dans l'ordre dès que possible.

Si cette liste de scripts qui s'exécutent dans l'ordre dès que possible n'est pas encore vide et la première entrée a déjà été marqué comme prêt, puis revenir à l'étape marqué d'exécution.

Si l'élément possède un attribut src de L'élément doit être ajouté à la ensemble de scripts à exécuter dès que possible du Document de l'élément script au moment de la préparer un script algorithme commencé.

La tâche, la mise en réseau de la tâche source des lieux sur la tâche de la file d'attente une fois la récupération de l'algorithme a complété doit exécuter le bloc de script et ensuite, retirer l'élément de l'ensemble de scripts qui seront exécutés dès que possible.

Sinon, L'agent utilisateur doit exécuter immédiatement le bloc de script, même si d'autres scripts sont déjà en cours d'exécution.

0 votes

Merci pour la réponse, mais le problème est que le script est ajoutés dynamiquement à la page, ce qui signifie il est considéré comme asynchrone . Ou cela ne fonctionne-t-il que dans <head> ? Et d'après mon expérience, ils sont également exécutés dans l'ordre des documents ?

0 votes

@Bergi - S'il est ajouté dynamiquement, alors il est asynchrone et l'ordre d'exécution est indéterminé, sauf si vous écrivez du code pour le contrôler.

0 votes

Juste, Kolink déclare le contraire...

42voto

jdc91 Points 5

Un excellent résumé par @addyosmani

enter image description here

Copié sans vergogne de https://addyosmani.com/blog/script-priorités/

16voto

Niet the Dark Absol Points 154811

Le navigateur exécutera les scripts dans l'ordre où il les trouve. Si vous appelez un scripts externe, il bloquera la page jusqu'à ce que le scripts ait été chargé et exécuté.

Pour tester ce fait :

// file: test.php
sleep(10);
die("alert('Done!');");

// HTML file:
<script type="text/javascript" src="test.php"></script>

Les scripts ajoutés dynamiquement sont exécutés dès qu'ils sont annexés au document.

Pour tester ce fait :

<!DOCTYPE HTML>
<html>
<head>
    <title>Test</title>
</head>
<body>
    <script type="text/javascript">
        var s = document.createElement('script');
        s.type = "text/javascript";
        s.src = "link.js"; // file contains alert("hello!");
        document.body.appendChild(s);
        alert("appended");
    </script>
    <script type="text/javascript">
        alert("final");
    </script>
</body>
</html>

L'ordre des alertes est "annexé" -> "bonjour !" -> "final"

Si dans un script vous tentez d'accéder à un élément qui n'a pas encore été atteint (exemple : <script>do something with #blah</script><div id="blah"></div> ), vous obtiendrez une erreur.

Globalement, oui, vous pouvez inclure des scripts externes et ensuite accéder à leurs fonctions et variables, mais seulement si vous sortez de l'écran courant. <script> et en commencer un nouveau.

0 votes

Je peux confirmer ce comportement. Mais il y a des indications sur nos pages de commentaires, que cela pourrait ne fonctionner que lorsque test.php est mis en cache. Connaissez-vous des liens de spécification/référence à ce sujet ?

4 votes

Link.js n'est pas bloquant. Utilisez un script similaire à celui de votre php pour simuler un long temps de téléchargement.

16 votes

Cette réponse est incorrecte. Ce n'est pas toujours le cas que "les scripts ajoutés dynamiquement sont exécutés dès qu'ils sont ajoutés au document". C'est parfois vrai (par exemple, pour les anciennes versions de Firefox), mais ce n'est généralement pas le cas. L'ordre d'exécution, comme mentionné dans la réponse de jfriend00, n'est pas déterminé.

12voto

FlyOn Points 451

Après avoir testé de nombreuses options, j'ai trouvé que la solution simple suivante consiste à charger les scripts dynamiquement dans l'ordre où ils sont ajoutés dans tous les navigateurs modernes.

loadScripts(sources) {
    sources.forEach(src => {
        var script = document.createElement('script');
        script.src = src;
        script.async = false; //<-- the important part
        document.body.appendChild( script ); //<-- make sure to append to body instead of head 
    });
}

loadScripts(['/scr/script1.js','src/script2.js'])

1voto

Panu Viljamaa Points 11

J'ai eu du mal à comprendre comment faire en sorte qu'un module intégré -script s'exécute avant que l'événement onload ne se produise. Les réponses ci-dessus m'ont beaucoup aidé mais permettez-moi d'ajouter une réponse partielle sur ce qui a résolu mon problème particulier de mauvaise compréhension de "l'ordre de chargement et d'exécution des script".

J'ai d'abord utilisé ... ce qui a causé un problème étrange : cela fonctionnait lors du chargement normal de la page, mais pas lors de son exécution dans le débogueur de FireFox. Cela rendait le débogage très difficile.

Note : scripts dont le type est "module" ont toujours un attribut implicite "deferred" qui signifie qu'ils n'arrêtent pas le parsing du html, ce qui signifie que le onload-event peut se produire avant que le scripts ne soit exécuté. Ce n'est pas ce que je voulais. Mais je voulais utiliser type="module" pour rendre mes fonctions et variables JavaScript non exportées invisibles aux autres scripts sur la même page.

J'ai essayé différentes options mais grâce aux réponses ci-dessus, j'ai compris que si vous ajoutez l'attribut async à un script de type module, cela signifie que le script se charge de manière asynchrone MAIS qu'une fois chargé, il s'exécute immédiatement.

Mais dans mon cas, il s'agissait d'un script intégré dans une page HTML. DONC, cela signifiait que rien n'avait besoin d'être chargé de manière "asynchrone". Il était déjà chargé avec la page, puisqu'il y était intégré. Par conséquent, avec ce changement, il est exécuté immédiatement, ce qui est ce que je voulais.

Je pense donc qu'il est utile de souligner ce cas spécifique car il est quelque peu contre-intuitif : Pour qu'un script embarqué soit exécuté IMMÉDIATEMENT, vous devez ajouter l'attribut ASYNC à sa balise.

D'ordinaire, on pourrait penser que le terme "asynchrone" signifie que quelque chose se produit de manière asynchrone, dans un ordre indéterminé, et non pas immédiatement. Mais ce qu'il faut comprendre, c'est que "async" entraîne un CHARGEMENT asynchrone, mais une EXÉCUTION immédiate une fois le chargement terminé. Et lorsque le script est intégré, aucun chargement ne doit être effectué, et vous obtenez donc une exécution immédiate.

Résumé : Utilisation

     <script type="module" async> ... </script>

pour qu'un module-script intégré à une page HTML s'exécute immédiatement.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X