Sie haben wahrscheinlich ES-Module in der modernen JavaScript-Entwicklung verwendet, aber kennen Sie die Geschichte hinter ihrer Entwicklung? Wenn Sie den Weg von den frühen JavaScript-Praktiken bis zum heutigen Modulsystem verstehen, werden Sie verstehen, wie weit wir gekommen sind und warum ES-Module bahnbrechend sind.
Wir schreiben das Jahr 1995, vier Jahre nach der Erstellung der ersten Webseite. Die meisten Websites waren einfach – statische Seiten mit Text und minimaler Interaktivität. Entwickler suchten jedoch schnell nach Möglichkeiten, Webseiten dynamischer zu gestalten.
In dieser Umgebung beauftragte Netscape (der damals dominierende Webbrowser) Brendan Eich mit der Entwicklung einer Skriptsprache, die direkt im Browser ausgeführt werden sollte. Dies führte zur Geburt von JavaScript, einer Sprache, die einfach und zugänglich sein soll, insbesondere für Nicht-Programmierer wie Webdesigner. Und so tat er es und stellte die erste Version in 10 Tagen fertig.
Ursprünglich war JavaScript dazu gedacht, Webseiten kleine Verbesserungen wie die Formularvalidierung hinzuzufügen, ohne dass Daten an den Server weitergeleitet werden mussten. Als Websites jedoch interaktiver wurden, wuchs JavaScript schnell über seinen ursprünglichen Zweck hinaus.
In den Anfängen befand sich der gesamte JavaScript-Code im globalen Bereich. Je mehr Entwickler derselben Seite Code hinzufügten, desto größer wurde das Risiko von Namenskonflikten. Wenn mehrere Skripte dieselbe Variable oder denselben Funktionsnamen verwenden, könnte der Code auf unerwartete Weise kaputt gehen.
Um dies zu bewältigen, griffen Entwickler auf Namenskonventionen zurück, um Kollisionen zu verhindern. Wenn ein Codeabschnitt nur einmal ausgeführt werden sollte, wurde er von Entwicklern oft in einen IIFE (Immediately Invoked Function Expression) verpackt. Dadurch blieben Funktionen und Variablen innerhalb der Funktion gültig und verhinderten, dass sie den globalen Namespace verschmutzten.
(function init() { function getData(){ // ... } })()
Das war damals gut genug, da die meisten Websites serverseitig mit sehr wenig clientseitiger Logik gerendert wurden.
Im Jahr 2008 erstellte Ryan Dahl Node.js, eine JavaScript-Laufzeitumgebung zum Erstellen serverseitiger Anwendungen. Dies eröffnete eine völlig neue Welt an Möglichkeiten, aber das Fehlen eines Modulsystems bedeutete, dass Entwickler Schwierigkeiten hatten, große Codebasen zu verwalten.
Im Jahr 2009 wurde CommonJS eingeführt, um dieses Problem serverseitig zu lösen. Das CommonJS-Modulsystem ermöglichte es Entwicklern, Module zu definieren, Funktionen bereitzustellen und andere Module zu importieren. Hier ist ein Beispiel dafür, wie es funktioniert hat:
const math = require("./math"); function subtract(a,b) { return math.add(a,-b); } module.exports = { subtract: subtract }
Mit CommonJS wird jede Datei als eigenes Modul behandelt und Module werden mit der Funktion require importiert und mit module.exports exportiert.
Zu den wichtigsten Funktionen von CommonJS gehören:
Die Dateierweiterung ist optional, wenn ein Modul erforderlich ist (z. B. sucht require('./math') automatisch nach math.js).
Das Laden des Moduls erfolgt synchron, d. h. das Programm wartet auf das Laden des Moduls, bevor es mit der Ausführung fortfährt.
Später in diesem Artikel werden wir sehen, warum Ryan Dahl zugegeben hat, diese beiden Designentscheidungen zu bereuen.
Etwa zur gleichen Zeit wurde ein weiteres Modulsystem namens AMD (Asynchronous Module Definition) entwickelt. Während sich CommonJS hauptsächlich auf serverseitiges JavaScript konzentrierte, wurde AMD für die Verarbeitung von clientseitigem JavaScript im Browser entwickelt.
Das Hauptmerkmal von AMD war seine Fähigkeit, Module asynchron zu laden. Dadurch konnte der Browser jeweils nur das für eine Seite benötigte JavaScript laden und so die Leistung verbessern, indem die anfängliche Ladezeit der Seite verkürzt wurde. Es löste auch Probleme im Zusammenhang mit der Abhängigkeitsauflösung und stellte sicher, dass ein Modul erst dann ausgeführt wurde, wenn seine Abhängigkeiten vollständig geladen waren.
Zu den Vorteilen von AMD gehören:
Mit dem Aufkommen von npm im Jahr 2010 (einem Paketmanager für serverseitiges JavaScript) wurde die Notwendigkeit deutlich, Code im Browser und auf dem Server gemeinsam zu nutzen. Hier kommt Browserify ins Spiel, ein Tool, das es Entwicklern ermöglicht, CommonJS-Module im Browser zu verwenden, indem es den Code so umwandelt, dass er mit der Browserumgebung kompatibel ist.
Mit den beiden konkurrierenden Standards CommonJS und AMD. Es bestand Bedarf an einem Einzelmodulsystem, das überall funktionieren kann, ohne dass ein Build-Schritt erforderlich ist. Und im Jahr 2011 wurde die Universal Module Definition (UMD) eingeführt.
UMD kombinierte das Beste aus beiden Welten und ermöglichte es Entwicklern, ein Modul zu schreiben, das ausgeführt werden kann in:
UMD erfreute sich bei Bibliotheksautoren großer Beliebtheit und wurde von namhaften Bibliotheken wie Lodash, Underscore.js, Backbone.js und Moment.js übernommen. UMD hatte jedoch einige erhebliche Nachteile. Es löste zwar das Kompatibilitätsproblem, brachte jedoch die Komplexität der Verwaltung beider Systeme mit sich und übernahm die Probleme von AMD und CommonJS.
Im Jahr 2015 wurden ES Modules (ESM) als Teil des ECMAScript-Standards eingeführt und boten endlich ein natives Modulsystem für JavaScript. Bis 2017 unterstützten alle großen Browser ES-Module, und im Jahr 2020 kam auch Node.js hinzu.
Sehen wir uns an, warum ES-Module die besten sind:
Der folgende UMD-Code:
(function init() { function getData(){ // ... } })()
kann jetzt reduziert werden auf:
(function init() { function getData(){ // ... } })()
Um fair zu sein, hat niemand tatsächlich UMD so geschrieben. Sie verwendeten Tools wie umdify, um diesen Code zu generieren. Aber da ES-Module integriert sind, können wir den Build-Schritt überspringen und eine kleinere Bundle-Größe haben.
ES-Module sind statisch, was bedeutet, dass Tools die Codestruktur zur Kompilierungszeit analysieren können, um festzustellen, welcher Code verwendet wird und welcher nicht. Dies ermöglicht Tree-Shaking, bei dem ungenutzter Code aus dem endgültigen Paket entfernt wird.
Da CommonJS- und AMD-Module dynamisch sind (zur Laufzeit ausgewertet), ist Tree Shaking bei diesen Systemen viel weniger effizient, was häufig zu größeren Bundles führt.
Beim Importieren eines Moduls mit CommonJS ist die Angabe der Dateierweiterung optional.
const math = require("./math"); function subtract(a,b) { return math.add(a,-b); } module.exports = { subtract: subtract }
Aber worauf bezieht sich Mathematik eigentlich? Handelt es sich um eine JavaScript-Datei? Eine JSON-Datei? Eine index.js-Datei in einem Math-Verzeichnis?
Bei der Verwendung statischer Analysetools wie ES Lint, Typescript oder Prettier wird jede Anforderung zu einem Ratespiel.
Ist es math.js?
ist es math.jsx?
ist es math.cjs?
ist es math.mjs?
ist es math.ts?
ist es math.tsx?
ist es math.mts?
ist es math.cts?
ist es math/index.js?
ist es math/index.jsx?
Sie verstehen schon.
Das Lesen einer Datei ist teuer. Es ist viel weniger leistungsfähig als das Lesen aus dem Speicher. Das Importieren von math/index.js führt zu 9 IO-Operationen statt 1! Und dieses Ratespiel verlangsamt unsere Tools und beeinträchtigt die Entwicklererfahrung.
In ES-Modulen vermeiden wir dieses Durcheinander, indem wir Dateierweiterungen obligatorisch machen.
Im Gegensatz zu CommonJS, das Module synchron lädt (und den gesamten Prozess blockiert, bis das Modul geladen ist), sind ES-Module asynchron. Dadurch kann JavaScript weiterhin ausgeführt werden, während das Modul im Hintergrund geladen wird, was die Leistung verbessert – insbesondere in Umgebungen wie Node.js.
Trotz der klaren Vorteile war die Einführung von ES-Modulen keine einfache Aufgabe. Aus diesem Grund hat die Umstellung so lange gedauert:
Der Wechsel von CommonJS- zu ES-Modulen war keine triviale Änderung, insbesondere bei großen Projekten. Die Syntaxunterschiede in Kombination mit der Notwendigkeit der Toolunterstützung machten die Migration zu einem erheblichen Aufwand.
Node.js brauchte 5 Jahre, um ES-Module vollständig zu unterstützen. Während dieser Zeit mussten Entwickler die Kompatibilität sowohl mit CommonJS (auf dem Server) als auch mit ES-Modulen (auf dem Browser) aufrechterhalten. Diese doppelte Unterstützung sorgte für große Spannungen im Ökosystem.
Auch nachdem Node.js die Unterstützung für ES-Module hinzugefügt hatte, konnten CommonJS-Module keine ES-Module laden. Während ES-Module CommonJS-Module laden konnten, waren die beiden Systeme nicht vollständig interoperabel, was den Paketautoren, die beide Systeme unterstützen mussten, zusätzliche Kopfschmerzen bereitete.
Die Zukunft von JavaScript-Modulen ist rosig, und hier sind einige wichtige Entwicklungen, die ES-Module in Zukunft zum dominierenden System machen werden:
In Node.js 23 haben wir endlich die Möglichkeit, ES-Module von CommonJS zu laden.
Es gibt eine kleine Einschränkung: ES-Module, die das Warten auf oberster Ebene verwenden, können nicht in CommonJS importiert werden, da das Warten nur in asynchronen Funktionen verwendet werden kann und CommonJS synchron ist.
Eine neue Javascript-Paketregistrierung, die mit npm konkurriert. Es hat viele Vorteile gegenüber npm, auf die ich hier nicht näher eingehen werde. Das Interessante ist jedoch, dass Sie nur ES-Modulpakete hochladen dürfen. Es besteht keine Notwendigkeit, die alten Standards zu unterstützen.
Der Weg von globalen Scope-Hacks zu modernen ES-Modulen hat die Art und Weise verändert, wie wir JavaScript strukturieren. Nach jahrelangen Experimenten mit CommonJS, AMD und UMD haben sich ES-Module als klarer Standard herausgestellt, der eine einfachere Syntax, bessere Optimierung und verbesserte Leistung bietet.
Obwohl die Migration zu ES-Modulen eine Herausforderung darstellte, insbesondere aufgrund der Node.js-Unterstützung und der Ökosystemkompatibilität, sind die Vorteile unbestreitbar. Da Node.js 23 die Interoperabilität verbessert und neue Tools wie JSR ein einheitliches Modulsystem fördern, werden ES-Module zum Standard für JavaScript.
Da wir weiterhin ES-Module nutzen, können wir uns auf saubereren, schnelleren und besser wartbaren Code freuen, der eine neue Ära der Modularität in der JavaScript-Entwicklung einläutet.
Das obige ist der detaillierte Inhalt vonEine kurze Geschichte der ES-Module. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!