W3docs

JavaScript export und import

JavaScript export und import erklärt: benannte Exporte, Default-Exporte, Umbenennen mit as, Namespace- und dynamische Importe, Re-Exporte und häufige Fallstricke — mit ausführbaren Beispielen.

In modernem JavaScript sind die Schlüsselwörter export und import das Mittel, um ein Programm auf viele Dateien aufzuteilen und wieder zusammenzufügen. Eine Datei, die sie verwendet, ist ein ES-Modul: Anstatt alles in ein einziges riesiges Skript zu packen, hält man jedes Stück Funktionalität in einer eigenen Datei, deklariert, was nach außen geteilt wird (export), und importiert anderswo nur das, was benötigt wird (import).

Dieser Leitfaden behandelt jede Form, der man in der Praxis begegnet — benannte Exporte, Default-Exporte, Umbenennen mit as, Namespace-Importe, Re-Exporte und dynamisches import() — sowie die Fallstricke, über die man stolpert. Jedes Beispiel ist klein und ausführbar.

Warum überhaupt Module?

Vor ES-Modulen bedeutete das Teilen von Code, Dinge an das globale Objekt zu hängen und zu hoffen, dass Namen nicht kollidierten. Module beheben das:

  • Kapselung — alles, was man nicht exportiert, bleibt privat in der Datei.
  • Explizite Abhängigkeiten — die import-Zeilen am Anfang einer Datei sind eine präzise Liste dessen, worauf sie angewiesen ist.
  • Einmalige Auswertung — der Code auf oberster Ebene eines Moduls wird einmal ausgeführt, egal wie viele Dateien es importieren; das Ergebnis wird zwischengespeichert und geteilt.

Um Module im Browser auszuführen, lädt man die Einstiegsdatei mit type="module":

<script src="app.js" type="module"></script>

In Node.js benennt man Dateien entweder .mjs oder setzt "type": "module" in package.json.

Benannte Exporte

Ein benannter Export exportiert eine Bindung unter ihrem eigenen Namen. Ein Modul kann beliebig viele benannte Exporte haben.

// math.js
export const PI = 3.14159;

export function add(a, b) {
  return a + b;
}

// You can also list exports at the bottom:
const subtract = (a, b) => a - b;
export { subtract };

Benannte Exporte werden in geschweiften Klammern importiert, mit exakt denselben Namen:

// app.js
import { add, PI } from './math.js';

console.log(add(2, 3)); // 5
console.log(PI);        // 3.14159
Info

Die Namen müssen mit dem Export übereinstimmen. import { Add }, wenn der Export add ist, wirft einen SyntaxError — einen solchen benannten Export gibt es nicht.

Umbenennen mit as

Wenn zwei Module denselben Namen exportieren oder ein Name unhandlich ist, kann man beim Import (oder Export) mit as umbenennen:

// Rename on import
import { add as sum } from './math.js';
console.log(sum(1, 1)); // 2
// Rename on export
const internalAdd = (a, b) => a + b;
export { internalAdd as add };

Default-Exporte

Ein Default-Export ist die einzige „Hauptsache", die ein Modul bereitstellt. Ein Modul kann höchstens einen Default-Export haben.

// multiply.js
export default function multiply(a, b) {
  return a * b;
}

Beim Importieren eines Default-Exports verwendet man keine geschweiften Klammern, und man wählt den lokalen Namen selbst:

// app.js
import multiply from './multiply.js'; // any name works
console.log(multiply(4, 5)); // 20

Da der Importierende den Namen vergibt, werden Default-Exporte häufig verwendet, wenn eine Datei eine Sache repräsentiert — eine einzelne Klasse, eine einzelne React-Komponente, ein einzelnes Konfigurationsobjekt.

Default- und benannte Exporte mischen

Ein Modul kann beides haben. Das Default-Export steht außerhalb der Klammern, benannte darin:

// user.js
export default class User { /* ... */ }
export const ADMIN_ROLE = 'admin';
// app.js
import User, { ADMIN_ROLE } from './user.js';
Info

Bevorzuge standardmäßig benannte Exporte. Sie machen Importe selbstdokumentierend und ermöglichen es Werkzeugen (Autovervollständigung, „Referenzen suchen", Tree-Shaking), zuverlässig zu funktionieren. Greife auf einen Default-Export nur zurück, wenn eine Datei wirklich eine Sache exportiert.

Importtechniken

Namespace-Import (import * as)

Um alles, was ein Modul exportiert, als einzelnes object zu erfassen, verwendet man einen Namespace-Import:

// app.js
import * as math from './math.js';

console.log(math.add(5, 3)); // 8
console.log(math.PI);        // 3.14159

Jeder benannte Export wird zu einer Eigenschaft von math. Ein Default-Export, falls vorhanden, erscheint als math.default. Dies bietet sich an, wenn ein Modul viele verwandte Hilfsfunktionen gruppiert.

Dynamischer Import (import())

Die import-Anweisung oben ist statisch — sie wird vor allem anderen ausgeführt, und ihr Pfad muss ein String-Literal sein. Manchmal möchte man ein Modul bei Bedarf laden: erst wenn der Nutzer eine Funktion öffnet oder basierend auf einer Laufzeitbedingung. Dafür verwendet man import() als Funktion. Es gibt ein Promise zurück, das zum Namespace-object des Moduls aufgelöst wird:

// Load a heavy module only when needed
async function openEditor() {
  const editor = await import('./editor.js');
  editor.init();
}

Dynamischer Import funktioniert innerhalb von Funktionen und Bedingungen — Stellen, an denen ein statisches import nicht erlaubt ist — und ist damit die Grundlage für Code-Splitting und Lazy Loading.

Re-Exporte

Eine „Barrel"-Datei kann Exporte aus mehreren Modulen sammeln und weiterleiten, sodass Konsumenten von einem einzigen Ort importieren:

// shapes/index.js
export { Circle } from './circle.js';
export { Square } from './square.js';
export { default as Triangle } from './triangle.js'; // re-export a default as a name
// app.js
import { Circle, Square, Triangle } from './shapes/index.js';

Häufige Fallstricke

  • Erweiterungen sind im Browser und in Node.js ESM wichtig. import { x } from './math' schlägt fehl; man schreibt './math.js'.
  • import/export funktionieren nur auf der obersten Ebene eines Moduls — nicht innerhalb eines if-Blocks oder einer Funktion. Für bedingtes Laden verwendet man dynamisches import().
  • Importe sind schreibgeschützte Live-Bindungen. Man kann einen importierten Wert lesen, aber nicht neu zuweisen; add = 5 auf einem Import wirft einen Fehler.
  • Ein Default-Export ist nicht automatisch benannt. export default add exportiert den Wert, nicht den Namen add; der Importierende wählt den lokalen Namen.
  • Module werden verzögert ausgeführt und laufen automatisch im Strict Mode — kein 'use strict' erforderlich.

Ein ausgearbeitetes Beispiel: ein Mini-Bibliothekssystem

Hier exportieren mehrere Module jeweils ein array von Buch-objects, und eine Hauptdatei importiert und kombiniert sie.

index.html:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <title>Interactive Library App</title>
</head>
<body>
  <h1>Interactive Library App</h1>
  <button id="loadFiction">Load Fiction</button>
  <button id="loadSciFi">Load Sci-Fi</button>
  <div id="bookList"></div>
  <script src="app.js" type="module"></script>
</body>
</html>

fiction.js:

export const fictionBooks = [
  { title: 'Pride and Prejudice', author: 'Jane Austen' },
  { title: 'To Kill a Mockingbird', author: 'Harper Lee' }
];

sciFi.js:

export const sciFiBooks = [
  { title: 'Dune', author: 'Frank Herbert' },
  { title: 'Neuromancer', author: 'William Gibson' }
];

app.js:

import { fictionBooks } from './fiction.js';
import { sciFiBooks } from './sciFi.js';

function displayBooks(books) {
  const list = document.getElementById('bookList');
  list.innerHTML = '';
  books.forEach(book => {
    const item = document.createElement('div');
    item.textContent = `${book.title} by ${book.author}`;
    list.appendChild(item);
  });
}

document
  .getElementById('loadFiction')
  .addEventListener('click', () => displayBooks(fictionBooks));
document
  .getElementById('loadSciFi')
  .addEventListener('click', () => displayBooks(sciFiBooks));

Jede Kategorie lebt in ihrem eigenen Modul und exportiert eine function oder Daten, und app.js führt sie zusammen. Diese Aufteilung von Daten und Verhalten hält jede Datei klein und macht das Hinzufügen neuer Kategorien trivial.

Best Practices

  1. Benannte Exporte bevorzugen — damit dokumentieren sich Importe selbst und Tree-Shaking funktioniert.
  2. Eine Verantwortlichkeit pro Modul — eine Datei sollte sich in einem Satz beschreiben lassen.
  3. Barrel-Dateien sparsam einsetzen — sie ordnen Importe, können aber bei übermäßigem Einsatz das Tree-Shaking beeinträchtigen.
  4. Umfangreichen oder selten genutzten Code mit dynamischem import() lazy laden.
  5. Immer Dateiendungen angeben in relativen Pfaden.

Fazit

Benannte Exporte geben einem viele Bindungen pro Datei, Default-Exporte geben einem einen „Haupt"-Export, und import (mit as, * as und dynamischem import()) bietet präzise Kontrolle darüber, was wann hereinkommt. Zusammen verwandeln sie einen Haufen Skripte in einen wartbaren, tree-shakeablen Modulgraphen. Als Nächstes erfährt man, wie Module im Browser geladen werden, in Module — Einführung.

Übungen

Übung
Welche Aussagen über export und import in JavaScript sind korrekt?
Welche Aussagen über export und import in JavaScript sind korrekt?
Was this page helpful?