Einführung in JavaScript-Module
JavaScript-Module helfen dir, große Projekte zu verwalten, indem du Code in separate, wiederverwendbare Teile aufteilst. Diese Anleitung zeigt, wie du Module effektiv einsetzt.
JavaScript-Module helfen dir, große Code-Projekte zu verwalten, indem du deinen Code in separate, wiederverwendbare Teile aufteilst. Diese Anleitung zeigt, wie du JavaScript-Module effektiv einsetzt, um deine Programmierkenntnisse und Projektorganisation zu verbessern.
Diese Seite erklärt, was ein Modul ist, die import/export-Syntax, den Unterschied zwischen benannten und Standard-Exporten, wie der Modulscope funktioniert, die älteren Modulsysteme, denen du noch begegnen wirst (CommonJS und AMD), sowie ein vollständiges Beispiel, das du ausführen kannst.
JavaScript-Module verstehen
Ein Modul ist schlicht eine Datei. Jede Datei erhält ihren eigenen privaten Scope: Variablen und Funktionen, die du innerhalb eines Moduls deklarierst, sind außerhalb nicht sichtbar, es sei denn, du exportierst sie explizit. Um etwas aus einem anderen Modul zu verwenden, importierst du es. Das ist die Grundlage jeder anderen Idee auf dieser Seite.
Module bringen außerdem zwei automatische Verhaltensweisen mit, die du kennen solltest:
- Strict mode ist immer aktiv. Jedes Modul läuft so, als ob es mit
'use strict'begänne — versehentliche globale Variablen werfen einenReferenceError, statt stillschweigend eine Variable zu erzeugen. thisauf oberster Ebene istundefined, nicht das globale Objekt. Das ist eine der einfachsten Möglichkeiten, festzustellen, ob Code als Modul ausgeführt wird.
Im Folgenden behandeln wir die grundlegende Syntax und Verwendung von Modulen.
Einführung in die Modulsyntax
Module verwenden import- und export-Anweisungen, um Code zwischen Dateien auszutauschen.
Beispiel:
// Exporting functions
export const add = (a, b) => a + b;
export function multiply(a, b) {
return a * b;
}
// Importing them in another file
import { add, multiply } from './mathFunctions.js';Die export-Anweisung ermöglicht es, Teile deines Moduls anderen Dateien zugänglich zu machen. Die import-Anweisung erlaubt es dir, diese Teile dort einzubinden, wo du sie benötigst.
Verwende klare, aussagekräftige Namen für deine Module und Funktionen. Das macht deinen Code leichter lesbar und wartbar.
Standard-Exporte vs. benannte Exporte
Du kannst Standard-Exporte oder benannte Exporte verwenden, um verschiedene Teile deines Codes zu teilen. Benannte Exporte teilen mehrere Features über ihren Namen, und Standard-Exporte teilen ein einzelnes Feature ohne Namensangabe.
Beispiel:
// mathFunctions.js
export default function subtract(a, b) {
return a - b;
}
// Using the default export
import subtract from './mathFunctions.js';Das Schlüsselwort default wird verwendet, um eine einzelne Funktion oder Variable zu exportieren. Beim Importieren eines Standard-Exports kannst du einen beliebigen Namen verwenden.
Benannte Exporte eignen sich gut für Hilfsfunktionen, während Standard-Exporte nützlich für das einzelne Hauptfeature einer Datei sind, wie etwa eine React-Komponente oder eine Klasse.
Eine Datei kann einen Standard-Export mit beliebig vielen benannten Exporten kombinieren. Du importierst sie gemeinsam — den Standard-Export zuerst, die benannten in geschweiften Klammern:
// mathFunctions.js
export const add = (a, b) => a + b; // named export
export default function subtract(a, b) { // default export
return a - b;
}
// app.js
import subtract, { add } from './mathFunctions.js';Einige Regeln, die es sich zu merken lohnt:
- Ein Modul kann nur einen Standard-Export haben, aber viele benannte Exporte.
- Benannte Importe müssen den exakten exportierten Namen verwenden (oder mit
asumbenennen:import { add as sum } from './mathFunctions.js'). - Ein Standard-Import kann jeden beliebigen Namen verwenden, da der Standard-Export keinen festen Namen hat.
Umbenennen und Re-Exportieren
Du kannst beim Import oder Export mit as umbenennen und aus einem anderen Modul re-exportieren, um einen einzigen Einstiegspunkt (eine „Barrel"-Datei) zu erstellen:
// utils.js
export { add as sum } from './mathFunctions.js';
export { default as subtract } from './mathFunctions.js';
// app.js
import { sum, subtract } from './utils.js';Module für sauberen Code nutzen
Der Einsatz von Modulen kann deinen Code einfacher handhabbar machen, besonders wenn Projekte größer werden.
Verzeichnisstruktur
Eine gute Ordnerstruktur hilft dabei, deinen Code organisiert zu halten.
Beispiel:
/src
/components
/helpers
/models
/services
index.jsAbhängigkeiten verwalten
Das Verwalten von Abhängigkeiten bedeutet sicherzustellen, dass deine Code-Dateien korrekt zusammenarbeiten.
Beispiel:
// Webpack configuration for bundling modules
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{ test: /\.js$/, use: 'babel-loader' }
]
}
};Diese Konfiguration weist Webpack an, mit index.js zu beginnen, es und alle seine Abhängigkeiten in bundle.js zu bündeln und das Ergebnis im Ordner dist abzulegen.
Hinweis: Webpack-Konfigurationsdateien verwenden traditionell die CommonJS-Syntax (module.exports), auch in stark auf ES6 ausgerichteten Projekten.
Fortgeschrittene Modultechniken
Die Nutzung erweiterter Modulfunktionen kann deinen Code effizienter und einfacher verwaltbar machen.
Dynamische Imports
Dynamische Imports erlauben es dir, Code nur dann zu laden, wenn er gebraucht wird, was deine Anwendung beschleunigen kann.
Beispiel:
// Dynamically importing a module
// Assuming a button element exists
button.onclick = () => {
import('./messageModule.js')
.then(module => {
module.showMessage('Dynamic Import Executed!');
});
};Dieser Code lädt ein Modul nur dann, wenn eine Schaltfläche geklickt wird, was die anfänglichen Ladezeiten reduzieren kann.
Verwende dynamische Imports für Teile deiner App, die nicht sofort benötigt werden, wie etwa zusätzliche Features, auf die erst später zugegriffen wird.
import() gibt ein Promise zurück, sodass du es auch mit async/await verwenden kannst:
async function loadFeature() {
const module = await import('./messageModule.js');
module.showMessage('Loaded on demand');
}Siehe Dynamische Imports für eine ausführlichere Betrachtung.
Kommunikation zwischen Modulen
Module sollten in sich geschlossen sein, können aber über gemeinsam genutzte Ressourcen kommunizieren.
Beispiel:
// stateManager.js
export let state = { count: 0 };
// counter.js
import { state } from './stateManager.js';
state.count++;Dieser Code zeigt zwei Module, die ein Zustandsobjekt teilen. Da JavaScript-Module als Singletons gecacht werden, referenzieren beide Dateien exakt dasselbe Objekt im Speicher. Wenn ein Modul den Zustand ändert, sieht das andere die Änderung.
Hinweis: Dieses Beispiel mutiert das gemeinsame Objekt direkt. Für Produktions-Apps solltest du eine State-Management-Bibliothek oder ein reaktives Muster in Betracht ziehen, um Aktualisierungen vorhersehbar zu handhaben.
Modulsysteme und ihre heutige Verwendung verstehen
In der modernen Web-Entwicklung ist das Verständnis der verschiedenen Modulsysteme unerlässlich:
- CommonJS: Primär in Node.js für serverseitigen Code verwendet.
- AMD (Asynchronous Module Definition): Wird für das asynchrone Laden von Modulen verwendet, geeignet für Browser.
- ES6-Module: Der Standard in der modernen Web-Entwicklung, unterstützt sowohl synchrones als auch asynchrones Laden.
Beispiele für jedes Modulsystem
Um diese Konzepte in JavaScript zu veranschaulichen, zeigen wir Codeausschnitte für jeden Modultyp neben dem bestehenden ES6-Beispiel.
CommonJS-Beispiel:
// mathFunctions.js
exports.add = function(a, b) {
return a + b;
};
// app.js
const math = require('./mathFunctions.js');
console.log(math.add(5, 3));Erläuterung zu CommonJS: In diesem Beispiel verwenden wir exports, um die Funktion add außerhalb der Datei verfügbar zu machen. Dann verwenden wir in einer anderen Datei require, um die Funktion add einzubinden, damit wir sie verwenden können. Dieses System wird häufig in Node.js eingesetzt.
AMD-Beispiel:
// mathFunctions.js
define([], function() {
return {
add: function(a, b) {
return a + b;
}
};
});
// app.js
require(['mathFunctions'], function(math) {
console.log(math.add(5, 3));
});Erläuterung zu AMD: Dieses Beispiel verwendet define, um ein Modul ohne Abhängigkeiten zu deklarieren, und gibt ein object zurück, das die Funktion add enthält. require wird dann verwendet, um das Modul asynchron zu laden. Dies ist hilfreich für das dynamische Laden von Modulen im Browser.
ES6-Module-Beispiel:
// mathFunctions.js
export const add = (a, b) => a + b;
// app.js
import { add } from './mathFunctions.js';
console.log(add(5, 3));Erläuterung zu ES6-Modulen: Hier verwenden wir export, um die Funktion add verfügbar zu machen, und import, um sie in einer anderen Datei zu verwenden. Dies ist der moderne Standard für den Umgang mit Modulen in JavaScript und wird von den meisten Browsern unterstützt.
ES6-Module in Node.js aktivieren
Bei der Verwendung von ES6-Modulen in Node.js kannst du dieselbe import/export-Syntax nutzen, die typischerweise in der Frontend-JavaScript-Entwicklung verwendet wird. Dies ermöglicht eine einheitliche Modulsyntax sowohl in Client- als auch in Server-Umgebungen.
Um die ES6-Modulsyntax in Node.js zu verwenden, musst du sicherstellen, dass deine Umgebung dies unterstützt. Ab Node.js Version 14 sind ES6-Module stabil, und ab Version 16 sind sie standardmäßig aktiviert, wenn "type": "module" gesetzt ist. So richtest du es ein:
package.json aktualisieren: Füge in der package.json deines Node.js-Projekts die folgende Zeile hinzu:
"type": "module"Damit wird Node.js angewiesen, .js-Dateien standardmäßig als ES6-Module zu behandeln.
Dateiendungen: Verwende .js für deine Moduldateien oder explizit .mjs, wenn du es vorziehst. Node.js erkennt beide, aber wenn du die Einstellung "type": "module" verwendest, wird .js als ES6-Modul angenommen.
// mathFunctions.js
export function add(a, b) {
return a + b;
}
// app.js
import { add } from './mathFunctions.js';
console.log(add(5, 3)); // 8In Node.js ESM erfordern relative Imports die Dateiendung (./mathFunctions.js, nicht ./mathFunctions). Das unterscheidet sich von CommonJS, wo die Endung optional ist.
Häufige Fallstricke
Einige Modulverhaltensweisen verwirren Einsteiger. Sie zu kennen spart Debugging-Zeit:
- Relative Pfade benötigen im Browser eine Dateiendung.
import { add } from './math'funktioniert in vielen Bundlern, schlägt aber mit nativem Browser-ESM fehl — dort muss'./math.js'geschrieben werden. - Module werden einmalig ausgeführt und gecacht. Egal wie viele Dateien dasselbe Modul importieren, sein Code auf oberster Ebene wird nur einmal ausgeführt, und alle Importeure teilen dieselbe Instanz. Das ist es, was das obige Shared-State-Beispiel möglich macht.
- Imports sind schreibgeschützte Bindungen, keine Kopien. Du kannst einen importierten Wert nicht neu zuweisen (
add = 5wirft einen Fehler). Du siehst einen Live-Link zum Export — wenn das exportierende Modul den Wert ändert, sehen die Importeure den neuen Wert. import/exportmuss auf oberster Ebene stehen. Statischeimport- undexport-Anweisungen dürfen nicht innerhalb einesif-Blocks oder einer Funktion stehen. Wenn du bedingtes Laden benötigst, verwende stattdessen die dynamischeimport()-Funktion.- Modul-Scripts werden zurückgestellt. Ein
<script type="module">im Browser blockiert das Parsen nicht; es wird ausgeführt, nachdem das HTML geparst wurde, ähnlich wie das Hinzufügen vondefer.
Mehr zum immer aktiven Strict mode findest du unter Strict mode. Die vollständige Referenz für Export und Import findest du unter Export and Import.
Best Practices für die Verwendung von JavaScript-Modulen
- Einfach halten: Verwende klare, einfache Namen für deine Dateien und Exporte.
- Konsistent bleiben: Wende dieselben Muster und Strukturen in deinem gesamten Projekt an, um deinen Code vorhersehbar zu machen.
- Alles dokumentieren: Kommentiere deinen Code und dokumentiere, wie deine Module zu verwenden sind.
- Bei Bedarf optimieren: Überprüfe und optimiere deinen Code regelmäßig, wenn dein Projekt wächst.
Vollständiges Beispiel
Nachfolgend findest du ein vollständiges Beispiel, das alles integriert, was du in diesem Artikel gelernt hast.
Projektstruktur:
/src
/math
- mathFunctions.js
- app.js
index.htmlmathFunctions.js:
export const add = (a, b) => a + b;
export default function subtract(a, b) {
return a - b;
}app.js:
import subtract, { add } from './math/mathFunctions.js';
document.getElementById('add').addEventListener('click', function() {
const result = add(5, 3);
document.getElementById('result').textContent = `Adding: ${result}`;
});
document.getElementById('subtract').addEventListener('click', function() {
const result = subtract(5, 3);
document.getElementById('result').textContent = `Subtracting: ${result}`;
});index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JavaScript Module Example</title>
</head>
<body>
<button id="add">Add 5 + 3</button>
<button id="subtract">Subtract 5 - 3</button>
<div id="result"></div>
<script type="module" src="src/app.js"></script>
</body>
</html>Hinweis: ES-Module erfordern einen lokalen Entwicklungsserver (z. B. npx serve oder Vite), um im Browser ausgeführt zu werden, da CORS-Beschränkungen gelten. Das direkte Öffnen von index.html über file:// schlägt fehl.
Dieses Setup verwendet eine einfache Webseite mit Schaltflächen, um das Addieren und Subtrahieren von Zahlen mithilfe importierter Funktionen zu demonstrieren. Die Ergebnisse werden direkt auf der Seite angezeigt.
Erläuterung des Beispiels
- mathFunctions.js: Diese Datei enthält zwei Funktionen (
addundsubtract), die als Module exportiert werden.addist ein benannter Export, undsubtractist ein Standard-Export. - app.js: Diese Datei importiert die Funktionen aus
mathFunctions.jsund bindet sie an Button-Klick-Ereignisse, um Berechnungen durchzuführen, wenn der Benutzer mit der Seite interagiert. - index.html: Die HTML-Datei richtet die Benutzeroberfläche mit Schaltflächen und einem Anzeigebereich für Ergebnisse ein. Sie verlinkt
app.jsals Modul.
Dieses vollständige Beispiel demonstriert, wie JavaScript-Module in einer realen Anwendung strukturiert und verwendet werden können.
Fazit
JavaScript-Module sind ein leistungsstarkes Werkzeug zur Organisation und Pflege großer Web-Anwendungen. Durch das Verstehen und den angemessenen Einsatz verschiedener Modulsysteme kannst du die Skalierbarkeit und Wartbarkeit deines Projekts verbessern. Regelmäßiges Aktualisieren deiner Kenntnisse über Modulsyntax, Best Practices und fortgeschrittene Techniken stellt sicher, dass deine Entwicklungsfähigkeiten scharf bleiben und deine Projekte an der Spitze bleiben.