70 lines
4.0 KiB
Markdown
70 lines
4.0 KiB
Markdown
## Module System Fundamentals
|
|
|
|
**CommonJS** repräsentiert ein synchrones, dynamisches Module-Loading-Paradigma, das für Server-Umgebungen designed wurde, wo Filesystem-I/O relativ schnell ist. **ES Modules (ESM)** verkörpern ein asynchrones, statisches Module-System, das für Browser- und Server-Kontexte mit Compile-Time-Optimierungen architektiert wurde.
|
|
|
|
## Syntactic und Semantic Differences
|
|
|
|
**CommonJS** nutzt `require()` für Imports und `module.exports`/`exports` für Exports:
|
|
```javascript
|
|
// Dynamic import resolution
|
|
const dependency = require('./path/to/module');
|
|
// Export assignment
|
|
module.exports = { function: myFunction };
|
|
```
|
|
|
|
**ESM** verwendet deklarative `import`/`export`-Statements:
|
|
```javascript
|
|
// Static import declaration
|
|
import { namedExport } from './module.js';
|
|
// Named export declaration
|
|
export const myFunction = () => {};
|
|
```
|
|
|
|
## Loading Semantics und Timing
|
|
|
|
Der kritische Unterschied liegt darin, **wann** Module Resolution auftritt:
|
|
|
|
- **CommonJS**: Runtime Module Resolution mit synchronem Loading. Die `require()`-Calls werden während der Execution evaluiert, was conditional Imports und dynamic Module Paths ermöglicht.
|
|
|
|
- **ESM**: Parse-Time Static Analysis mit asynchronem Loading. Import Declarations werden gehoisted und während der Parsing-Phase resolved, bevor die Code-Execution beginnt.
|
|
|
|
## Module Graph Construction
|
|
|
|
**CommonJS** baut seinen Dependency Graph dynamisch durch Depth-First-Traversal während der Runtime Execution auf. Das kreiert einen **mutable Module Namespace**, wo Exports post-load modifiziert werden können.
|
|
|
|
**ESM** konstruiert einen static Module Graph während der Parsing-Phase durch drei distinkte Phasen:
|
|
1. **Construction**: Module parsen und Dependency Graph aufbauen
|
|
2. **Instantiation**: Memory für Exports allozieren und Live Bindings kreieren
|
|
3. **Evaluation**: Module Code ausführen und Exports populieren
|
|
|
|
## Binding Semantics
|
|
|
|
Hier wird's interessant:
|
|
|
|
**CommonJS** kreiert **Value Copies** - wenn du was importierst, kriegst du einen Snapshot des exported Value zu dem Moment. Mutations am Original propagieren nicht.
|
|
|
|
**ESM** etabliert **Live Bindings** - Imports sind Read-Only-References zu den tatsächlichen exported Values. Changes an Exports sind sofort für alle Importer sichtbar.
|
|
|
|
## Circular Dependency Handling
|
|
|
|
**CommonJS** handelt Cycles durch Partial Evaluation - Module returnen ihr aktuelles `exports`-Object, auch wenn nicht fully initialized, was potentially undefined Behavior verursachen kann.
|
|
|
|
**ESM** supportet Cycles durch Forward References in der Instantiation Phase, wobei der Access auf uninitialized Bindings `ReferenceError` wirft.
|
|
|
|
## Interoperability Challenges
|
|
|
|
Die **Dual Module Hazard** emergiert, wenn dasselbe Package in beiden Formaten gleichzeitig existiert, was potentially separate Instances kreiert und Singleton Patterns bricht. Node.js addressiert das durch:
|
|
|
|
- **Conditional Exports** in package.json
|
|
- **ESM Wrapper Patterns** für CommonJS Module
|
|
- **Dynamic import()** für das Loading von CommonJS aus ESM-Kontexten
|
|
|
|
## Performance Implications
|
|
|
|
**ESMs** Static Analysis enabled superior **Tree-Shaking** (Dead Code Elimination) und **Bundler Optimizations**. Die Upfront Parsing Cost zahlt sich in Production durch kleinere Bundle Sizes und bessere Runtime Performance aus.
|
|
|
|
**CommonJS's** Dynamic Nature verhindert viele Compile-Time-Optimizations, bietet aber Runtime Flexibility für Plugin Architectures und Conditional Loading Patterns.
|
|
|
|
TL;DR: CommonJS priorisiert Runtime Flexibility mit synchronem, dynamic Loading, während ESM für Static Analysis und Performance durch asynchrone, declarative Imports optimiert. Die Transition reflektiert JavaScripts Evolution von einer simplen Scripting Language zu einer Platform für komplexe, optimierte Applications.
|
|
|
|
Die Interop Story entwickelt sich noch, besonders around Top-Level Await und Package Dual-Mode Publishing Strategies. Understanding beider Systems bleibt essential für die Navigation durch den Current JavaScript Ecosystems Transitional State. |