JavaScript: Wie bindet man eine JavaScript-Datei in einer anderen ein?
JavaScript hat kein #include. Es hat Module, und im Browser hat es eine
Skript-Reihenfolge — und aus Letzterer kommen die meisten Fehler.
import
// helper.mjs
export const greet = n => `hello ${n}`;
// main.mjs
import { greet } from "./helper.mjs";
[helper.mjs body ran]
greet('world') : hello world | version: esm
import
ist die Antwort für alles, was heute geschrieben wird. Im Browser braucht es
<script type="module">, in Node die Endung .mjs oder "type": "module" in
der package.json.
Der Rumpf läuft einmal
-> imported three times in total, body printed once. Modules are cached.
[helper.mjs body ran] erscheint einmal, obwohl drei verschiedene Stellen das
Modul importieren. Ein Modul wird beim ersten Anfordern ausgewertet und das
Ergebnis gecacht — deshalb darf ein Modul gefahrlos Zustand halten, und deshalb
kann man mit einem Import eine Datei nicht “noch einmal ausführen”.
require
[helper.cjs body ran]
require('./helper.cjs') : hello world | version: cjs
require ist die ältere
CommonJS-Form. Nur in Node, nie ein Browser-Feature, und es cacht genauso. In
bestehendem Code begegnet es dir weiterhin.
import kann nicht bedingt sein, import() schon
a conditional static import is a SyntaxError
dynamic import() is a function call, so it CAN be conditional:
loaded on demand: esm
Ein statisches import wird gehoistet und aufgelöst, bevor irgendetwas aus der
Datei läuft, es kann also nicht in einem if stehen. Das ist keine willkürliche
Einschränkung, sondern genau das, was einem Bundler den Abhängigkeitsgraphen
verrät, ohne etwas auszuführen.
Wenn du wirklich bedingt oder verzögert laden willst, nimm
import():
Das ist ein Ausdruck, gibt ein Promise zurück und darf überall stehen.
Im Browser entscheidet die Reihenfolge
execution order : inline-1 -> inline-2
Einfache <script>-Tags laufen in Dokumentreihenfolge und blockieren dabei
jeweils den Parser. Also passiert Folgendes:
first script saw laterVar as : undefined
Das erste Skript sieht nicht, was das zweite definiert. Das ist die eigentliche
Ursache der meisten “X is not defined”-Fehler — keine fehlende Datei, sondern
eine Datei, die nach dem Code geladen wird, der sie benutzt.
defer und async
a.js defer=false async=false
b.js defer=true async=false
c.js defer=false async=true
- einfach: blockiert das Parsen, läuft sofort
defer: läuft nach dem Parsen, in Dokumentreihenfolgeasync: läuft, sobald es da ist, in keiner garantierten Reihenfolge
async ist das, was einen erwischt. Zwei async-Skripte, von denen eines das
andere braucht, funktionieren auf deiner Maschine und scheitern auf einer
langsameren Leitung, weil die Reihenfolge von der Ladezeit abhängt.
type : module | the spec defers module scripts even without the attribute
type="module" ist verzögert, ob du defer schreibst oder nicht.
Was man nicht tun sollte
Die alte Antwort — ein <script>-Element erzeugen und ins DOM hängen — ist
weiterhin im Umlauf. Sie funktioniert, aber nichts sagt dir, wann sie fertig
ist, die nächste Zeile deines Codes läuft also, bevor die Datei geladen ist.
Brauchst du diese Form, gibt dir import() ein Promise, auf das du warten
kannst.
Hinweis zu Netcup (Werbung)
Der deutsche Hoster Netcup bietet unter anderem günstige und zugleich leistungsstarke Webhosting Pakete, KVM-basierte Root Server und dezidierte Server an. Mit unseren Gutscheincodes kannst du noch mehr Geld sparen (6€ bei deiner ersten Bestellung, 30% Rabatt auf alle KVM-basierten Root Server, ...).