Frontend Labs
⋯

16. Modules: CommonJS vs ES Modules, bundlers, dynamic import()

Two module systems coexist in the JS ecosystem:

// CommonJS (Node's original system, synchronous, still everywhere in npm packages)
const fs = require("node:fs");
module.exports = { readConfig };

// ES Modules (the language standard, async-capable, works natively in browsers)
import fs from "node:fs";
export function readConfig() { /* ... */ }
export default class Config { /* ... */ }

Key differences: ESM is statically analyzable (imports/exports are determined before execution, enabling tree-shaking — bundlers can delete unused exports), is always strict mode, and has live bindings (an imported value updates if the exporting module changes it, unlike CommonJS's snapshot-at-require-time copy).

Dynamic import() returns a Promise and is the standard way to code-split — load a module only when it's actually needed:

button.addEventListener("click", async () => {
  const { openEditor } = await import("./editor.js");   // fetched only on first click
  openEditor();
});

Bundlers (Webpack, esbuild, Vite/Rollup) exist because: browsers didn't support ESM until ~2018 (Webpack predates browser ESM), node_modules has thousands of small files that are faster to serve as fewer bundles, and bundlers do tree-shaking, minification, and transform newer syntax down for older targets.

Generated from real-life-JS-guide.md · 8 Oct 2026 16:36