Modules JavaScript : import, export et ESM
Comprenez les ES modules : syntaxe import et export, usage dans le navigateur et Node.js, rôle des bundlers et bonnes pratiques pour découper votre code proprement.

Comprenez les ES modules : syntaxe import et export, usage dans le navigateur et Node.js, rôle des bundlers et bonnes pratiques pour découper votre code proprement.

Quand un fichier JavaScript grossit, tout le monde finit par avoir le même réflexe : « il faut découper ». Les ES modules (ESM) sont la façon standard de le faire en JavaScript moderne. Comprendre import et export, ce que le navigateur et Node.js en font, et pourquoi un bundler intervient encore, change réellement la maintenabilité d’un projet.
Ce guide reste pédagogique : assez de théorie pour ne pas naviguer à vue, assez de pratique pour ouvrir votre éditeur dès demain.
Un module est un fichier JavaScript qui déclare clairement ce qu’il expose au reste du projet et ce qu’il consomme ailleurs. Au lieu d’empiler des scripts globaux qui se marchent dessus, chaque fichier possède une frontière.
Avec les ES modules :
export ;import ;window ;Avant les modules, on utilisait des fonctions auto-invoquées, des espaces de noms maison, puis CommonJS (require et module.exports) côté Node.js. Aujourd’hui, ESM est le standard du langage. CommonJS reste présent dans de nombreux paquets npm historiques, d’où quelques frictions. Le guide MDN sur les modules fait référence.
Un fichier utils.js de plusieurs milliers de lignes devient illisible. Des modules date.js, format.js et api/client.js racontent une histoire, et un nouvel arrivant trouve plus vite où intervenir.
Exporter une fonction pure comme formatPrice(amount, locale) évite de la recopier dans trois pages, donc d’obtenir trois versions qui divergent.
Tester une fonction exportée est simple. Tester un script qui modifie des variables globales et touche au DOM dès son chargement l’est beaucoup moins.
Deux personnes peuvent travailler sur des modules distincts avec moins de conflits Git, à condition de respecter les frontières et d’éviter les imports circulaires.
Les modules permettent le tree-shaking : l’outil de build élimine le code inutilisé. En natif dans le navigateur, chaque fichier est une requête, d’où le rôle des bundlers en production. En développement, des outils comme Vite exploitent au contraire le découpage ESM pour un rechargement rapide.
// math.js
export function add(a, b) {
return a + b;
}
export const PI = 3.14159;
// app.js
import { add, PI } from './math.js';
console.log(add(2, 3), PI);
Les accolades ne sont pas du destructuring : elles ciblent des exports nommés, et le nom doit correspondre (sauf alias). La page MDN sur export et celle sur import détaillent toutes les variantes.
// Button.js
export default function Button(label) {
const btn = document.createElement('button');
btn.textContent = label;
return btn;
}
import Button from './Button.js';
Un module ne peut avoir qu’un seul export default. C’est pratique pour un composant principal ; pour des utilitaires, les exports nommés sont préférables, car l’auto-complétion les découvre mieux.
import { add as somme } from './math.js';
import * as math from './math.js';
math.add(1, 2);
Pour exposer une API publique depuis un dossier, un fichier d’index réexporte le nécessaire :
// components/index.js
export { default as Button } from './Button.js';
export { Card } from './Card.js';
Le reste du projet importe depuis ./components/index.js sans connaître l’arborescence interne.
Pour activer ESM dans une page :
<script type="module" src="/js/app.js"></script>
Quelques points importants, détaillés dans la page MDN de l’élément script :
defer ;./lib.js, /js/lib.js) ;.js est en général obligatoire en natif ;file:// casse souvent les imports, utilisez un petit serveur local.Un exemple minimal :
// app.js
import { greet } from './greet.js';
document.querySelector('#sortie').textContent = greet('Léa');
Pour comprendre ce qui se passe à l’exécution, lisez Comment fonctionne JavaScript dans le navigateur.
Node.js active ESM de deux façons : avec "type": "module" dans package.json, ou avec l’extension .mjs. CommonJS reste le comportement historique de nombreux projets, et mélanger les deux demande de la rigueur (imports dynamiques, paquets publiés dans les deux formats).
// Node.js en ESM
import fs from 'node:fs/promises';
import { config } from './config.js';
Beaucoup de nouveaux projets partent directement en ESM, tandis que les bases de code anciennes migrent progressivement.
Si le navigateur comprend les modules, pourquoi Webpack, Rollup, esbuild ou Vite ?
En pratique, vous écrivez en modules et l’outil produit les fichiers adaptés à la production.
import et export.require.module et moduleResolution.Inutile de tout maîtriser dès le premier jour. Commencez par ESM dans un petit projet front, puis explorez Node.js et TypeScript quand le besoin apparaît.
Imaginez une page produit avec un seul product.js de plusieurs centaines de lignes : carrousel, avis, ajout au panier, mesure d’audience. Un découpage raisonnable :
product/
carousel.js
reviews.js
cart.js
tracking.js
main.js
Le fichier main.js orchestre :
import { initCarousel } from './carousel.js';
import { initReviews } from './reviews.js';
import { bindAddToCart } from './cart.js';
initCarousel(document.querySelector('[data-carousel]'));
initReviews(document.querySelector('[data-reviews]'));
bindAddToCart(document.querySelector('[data-add-cart]'));
Chaque module expose une fonction d’initialisation claire. Vous pouvez désactiver le suivi en retirant un import, tester le carrousel isolément, et éviter qu’une variable index du carrousel n’écrase celle des avis. Pour manipuler les éléments de la page dans chaque module, voyez DOM JavaScript : manipuler la page correctement.
Si ce JavaScript est devenu une boule de neige sur un site existant, un audit de performance et de qualité aide à prioriser le découpage.
type="module" : sans cet attribut, import provoque une erreur dont le message parle souvent de « token inattendu » plutôt que de modules.import x from 'math.js' cherche un paquet, pas un fichier voisin. Pour un fichier local, préfixez par ./ ou ../.from './math' fonctionne souvent avec un bundler, pas en ESM natif.undefined au mauvais moment. Extrayez un troisième module partagé.fetch ou modifie le DOM dès l’import surprend. Exportez des fonctions et appelez-les depuis un main.import() pour le chargement différé réel (routes, fenêtres modales lourdes).utils.js attire tout, formatCurrency.js guide.main.js, index.js).Un module JavaScript (ES module) est un fichier aux frontières explicites : export ce que vous offrez, import ce que vous consommez. Ce modèle améliore la lisibilité, les tests, la collaboration et, avec le bon outillage, la livraison.
Commencez petit : découpez un script monolithique en trois modules à responsabilité claire, branchez-les avec type="module" et observez le confort gagné. Ajoutez ensuite un bundler ou TypeScript si le projet le justifie. Pour consolider les bases, relisez Variables, fonctions et objets en JavaScript puis l’asynchrone en JavaScript. Pour suivre un parcours complet, rejoignez Academy Yunt.
Non. React, Vue et les autres s’appuient sur les modules. ESM organise le code ; le framework organise l’interface et l’état.
Pas au début : un serveur local et type="module" suffisent. Le bundler devient utile avec TypeScript, JSX ou pour optimiser la production.
Le chargement des modules est géré par l’environnement : le code situé après un import statique s’exécute quand les dépendances sont prêtes. L'import() dynamique, lui, renvoie une promesse.
Oui, en front pur. npm devient pertinent dès que vous réutilisez des paquets tiers.
Vérifiez "type": "module", les extensions de fichiers et le format du paquet visé (ESM ou CommonJS). Le message d’erreur indique souvent le conflit.