webentwicklung-frage-antwort-db.com.de

Lade und führe die Reihenfolge der Skripte aus

Es gibt so viele verschiedene Möglichkeiten, JavaScript in eine HTML-Seite aufzunehmen. Ich kenne folgende Möglichkeiten:

  • inline-Code oder von externer URI geladen
  • enthalten in <head> oder <body> -Tag [ 1 , 2 ]
  • keine, defer oder async Attribute haben (nur externe Skripte)
  • in statischer Quelle enthalten oder dynamisch durch andere Skripte hinzugefügt (bei verschiedenen Analysezuständen, mit verschiedenen Methoden)

Ohne Browserskripte von der Festplatte, Javascript: URIs und onEvent- Attribute [], gibt es bereits 16 Alternativen, um JS ausführen zu lassen, und ich bin sicher, dass ich etwas vergessen habe.

Mir geht es nicht so sehr um schnelles (paralleles) Laden, sondern vielmehr um die Ausführungsreihenfolge (die möglicherweise von der Ladereihenfolge und Dokumentenreihenfolge abhängt). Gibt es eine gute (browserübergreifende) Referenz, die wirklich alle Fälle abdeckt? Z.B http://www.websiteoptimization.com/speed/Tweak/defer/ behandelt nur 6 von ihnen und testet hauptsächlich alte Browser.

Da ich befürchte, dass es keine gibt, ist hier meine spezifische Frage: Ich habe einige (externe) Head-Skripte zur Initialisierung und zum Laden von Skripten. Dann habe ich zwei statische Inline-Skripte am Ende des Körpers. Bei der ersten Methode hängt der Script Loader dynamisch ein anderes Script-Element (das auf externe Js verweist) an den Body an. Das zweite statische Inline-Skript möchte js aus dem hinzugefügten externen Skript verwenden. Kann man sich darauf verlassen, dass der andere hingerichtet wurde (und warum :-)?

237
Bergi

Wenn Sie Skripts nicht dynamisch laden oder als defer oder async markieren, werden die Skripts in der auf der Seite angegebenen Reihenfolge geladen. Es spielt keine Rolle, ob es sich um ein externes Skript oder ein Inline-Skript handelt - sie werden in der Reihenfolge ausgeführt, in der sie auf der Seite gefunden werden. Inline-Skripte, die nach externen Skripten eingehen, werden gehalten, bis alle externen Skripte, die vor ihnen eingegangen sind, geladen und ausgeführt wurden.

Asynchrone Skripts (unabhängig davon, wie sie als asynchrone Skripts angegeben sind) werden in einer unvorhersehbaren Reihenfolge geladen und ausgeführt. Der Browser lädt sie parallel und kann sie in beliebiger Reihenfolge ausführen.

Es gibt keine vorhersehbare Reihenfolge zwischen mehreren asynchronen Dingen. Wenn eine vorhersehbare Reihenfolge benötigt wird, muss sie durch Registrieren für Ladebenachrichtigungen aus den asynchronen Skripten und manuelles Sequenzieren von Javascript-Aufrufen beim Laden der entsprechenden Dinge codiert werden.

Wenn ein Skript-Tag dynamisch eingefügt wird, hängt das Verhalten der Ausführungsreihenfolge vom Browser ab. Sie können sehen, wie sich Firefox verhält in dieser Referenzartikel . Kurz gesagt, die neueren Versionen von Firefox verwenden standardmäßig ein dynamisch hinzugefügtes Skript-Tag für die asynchrone Ausführung, sofern das Skript-Tag nicht anders festgelegt wurde.

Ein Skript-Tag mit async kann ausgeführt werden, sobald es geladen ist. Tatsächlich kann der Browser den Parser anhalten und das Skript ausführen. Es kann also wirklich zu fast jeder Zeit ausgeführt werden. Wenn das Skript zwischengespeichert wurde, wird es möglicherweise fast sofort ausgeführt. Wenn es eine Weile dauert, bis das Skript geladen ist, wird es möglicherweise ausgeführt, nachdem der Parser abgeschlossen ist. Bei async ist zu beachten, dass es jederzeit ausgeführt werden kann und diese Zeit nicht vorhersehbar ist.

Ein Skript-Tag mit defer wartet, bis der gesamte Parser abgeschlossen ist, und führt dann alle mit defer markierten Skripts in der Reihenfolge aus, in der sie gefunden wurden. Auf diese Weise können Sie mehrere voneinander abhängige Skripte als defer markieren. Sie werden alle verschoben, bis der Dokumentparser abgeschlossen ist. Sie werden jedoch in der Reihenfolge ausgeführt, in der sie angetroffen wurden, wobei ihre Abhängigkeiten erhalten bleiben. Ich denke an defer, als würden die Skripte in eine Warteschlange gestellt, die nach dem Parser abgearbeitet wird. Technisch kann der Browser die Skripte jederzeit im Hintergrund herunterladen, sie werden den Parser jedoch erst ausführen oder blockieren, nachdem der Parser die Seite analysiert und Inline-Skripte analysiert und ausgeführt hat, die nicht mit defer oder async.

Hier ist ein Zitat aus diesem Artikel:

skripte, die mit Skripten eingefügt wurden, werden asynchron in IE und WebKit, aber synchron in Opera und Firefox vor 4.0 ausgeführt.

Der relevante Teil der HTML5-Spezifikation (für neuere kompatible Browser) ist hier . Dort steht viel über asynchrone Verhaltensweisen geschrieben. Offensichtlich gilt diese Spezifikation nicht für ältere Browser (oder Browser, die sich nicht bestätigen lassen), deren Verhalten Sie wahrscheinlich testen müssten, um festzustellen.

Ein Zitat aus der HTML5-Spezifikation:

Dann muss die erste der folgenden Optionen befolgt werden, die die Situation beschreiben:

Wenn das Element ein src-Attribut und das Element ein defer-Attribut hat und das Element als "vom Parser eingefügt" gekennzeichnet wurde und das Element kein asynchrones Attribut hat Das Element muss an das Ende der Liste der Skripte angefügt werden, die ausgeführt werden, wenn die Analyse des Dokuments abgeschlossen ist, das dem Dokument des Parsers zugeordnet ist, der das Element erstellt hat.

Die Task, die die Netzwerk-Task-Quelle in die Task-Warteschlange stellt, nachdem der Abrufalgorithmus abgeschlossen wurde, muss das Flag "Parser-fähig" des Elements setzen. Der Parser führt das Skript aus.

Wenn das Element ein src-Attribut hat und das Element als "vom Parser eingefügt" gekennzeichnet wurde und das Element kein asynchrones Attribut hat Das Element ist das ausstehende Parsing-Blocking-Skript des Dokuments des Parsers, der das Element erstellt hat. (Pro Dokument kann immer nur ein solches Skript vorhanden sein.)

Die Task, die die Netzwerk-Task-Quelle in die Task-Warteschlange stellt, nachdem der Abrufalgorithmus abgeschlossen wurde, muss das Flag "Parser-fähig" des Elements setzen. Der Parser führt das Skript aus.

Wenn das Element kein src-Attribut hat und das Element als "Parser eingefügt" gekennzeichnet wurde und das Dokument des HTML- oder XML-Parsers, der das Skriptelement erstellt hat, einen Stil hat Blatt, das Skripte blockiert Das Element ist das ausstehende Skript zum Blockieren der Analyse des Dokuments des Parsers, der das Element erstellt hat. (Pro Dokument kann immer nur ein solches Skript vorhanden sein.)

Setzen Sie das Flag "Bereit zum Parser" des Elements. Der Parser führt das Skript aus.

Wenn das Element ein src-Attribut hat, kein asynchrones Attribut hat und das Flag "Force-Async" nicht gesetzt ist , muss das Element hinzugefügt werden an das Ende der Liste der Skripte, die ausgeführt werden, um so schnell wie möglich das Dokument des Skriptelements zuzuordnen, als die Vorbereitung eines Skriptalgorithmus gestartet wurde.

Die Aufgabe, die die Netzwerkaufgabenquelle in die Aufgabenwarteschlange stellt, nachdem der Abrufalgorithmus abgeschlossen wurde, muss die folgenden Schritte ausführen:

Wenn das Element jetzt nicht das erste Element in der Liste der Skripte ist, das in der Reihenfolge ausgeführt wird, in der es oben hinzugefügt wurde, markieren Sie das Element als bereit, aber brechen Sie diese Schritte ab, ohne das Skript noch auszuführen.

Ausführung: Führen Sie den Skriptblock aus, der dem ersten Skriptelement in dieser Liste von Skripten entspricht, die so schnell wie möglich in der angegebenen Reihenfolge ausgeführt werden.

Entfernen Sie das erste Element aus dieser Liste von Skripten, die so schnell wie möglich ausgeführt werden.

Wenn diese Liste der Skripte, die so schnell wie möglich ausgeführt werden, noch nicht leer ist und der erste Eintrag bereits als bereit markiert wurde, kehren Sie zum Schritt mit der Bezeichnung "Ausführung" zurück.

Wenn das Element ein src-Attribut hat Das Element muss zu der Gruppe von Skripten hinzugefügt werden, die so schnell wie möglich vom Dokument des Skriptelements unter ausgeführt werden Der Zeitpunkt, zu dem die Vorbereitung eines Skriptalgorithmus gestartet wurde.

Die Aufgabe, die die Netzwerkaufgabenquelle in die Aufgabenwarteschlange stellt, nachdem der Abrufalgorithmus abgeschlossen wurde, muss den Skriptblock ausführen und dann das Element aus der Gruppe der Skripten entfernen, die so schnell wie möglich ausgeführt werden.

Andernfalls muss der Benutzeragent den Skriptblock sofort ausführen, auch wenn bereits andere Skripts ausgeführt werden.


Was ist mit Javascript-Modul-Skripten, type="module"?

Javascript unterstützt jetzt das Laden von Modulen mit folgender Syntax:

<script type="module">
  import {addTextToBody} from './utils.mjs';

  addTextToBody('Modules are pretty cool.');
</script>

Oder mit dem src Attribut:

<script type="module" src="http://somedomain.com/somescript.mjs">
</script>

Alle Skripte mit type="module" erhalten automatisch das Attribut defer. Dies lädt sie parallel (wenn nicht inline) zum anderen Laden der Seite herunter und führt sie dann der Reihe nach aus, aber nachdem der Parser abgeschlossen ist.

Modulskripten kann auch das Attribut async zugewiesen werden, mit dem Inline-Modulskripten so schnell wie möglich ausgeführt werden, ohne auf die Ausführung des Parsers und ohne auf die Ausführung des Skripts async in einer bestimmten relativen Reihenfolge zu warten zu anderen Skripten.

Es gibt ein ziemlich nützliches Timeline-Diagramm, das das Abrufen und Ausführen verschiedener Kombinationen von Skripten zeigt, einschließlich Modul-Skripten hier in diesem Artikel: Javascript Module Loading .

293
jfriend00

Der Browser führt die Skripte in der Reihenfolge aus, in der sie gefunden wurden. Wenn Sie ein externes Skript aufrufen, wird die Seite blockiert, bis das Skript geladen und ausgeführt wurde.

So testen Sie diese Tatsache:

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

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

Dynamisch hinzugefügte Skripte werden ausgeführt, sobald sie an das Dokument angehängt werden.

So testen Sie diese Tatsache:

<!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>

Die Reihenfolge der Warnungen ist "angefügt" -> "Hallo!" -> "final"

Wenn Sie in einem Skript versuchen, auf ein Element zuzugreifen, das noch nicht erreicht wurde (Beispiel: <script>do something with #blah</script><div id="blah"></div>) dann erhalten Sie eine Fehlermeldung.

Insgesamt können Sie ja externe Skripte einbinden und dann auf deren Funktionen und Variablen zugreifen, aber nur, wenn Sie das aktuelle <script> taggen und einen neuen starten.

11

Eine großartige Zusammenfassung von @ addyosmani

enter image description here

Schamlos kopiert von https://addyosmani.com/blog/script-priorities/

7
Dehan de Croos

Nachdem ich viele Optionen getestet habe, habe ich festgestellt, dass die folgende einfache Lösung die dynamisch geladenen Skripte in der Reihenfolge lädt, in der sie in allen modernen Browsern hinzugefügt wurden

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'])
1
Flion