Apprends à organiser ton code avec les modules ES — syntaxe import/export, exports nommés et par défaut, et modèles de modules.
À mesure que les applications grandissent, garder tout le code dans un seul fichier devient ingérable. Les modules te permettent de diviser le code en fichiers séparés, chacun avec sa propre portée.
Avantages des modules :
Avant les modules ES, les développeurs utilisaient divers modèles pour obtenir de la modularité :
IIFE (Immediately Invoked Function Expressions) :
const myModule = (function() {
const privateVar = 'hidden';
return { publicMethod: () => privateVar };
})();CommonJS (Node.js) :
const fs = require('fs');
module.exports = { myFunction };Les modules ES (ESM) sont maintenant le standard officiel pour les modules JavaScript, supportés nativement dans les navigateurs et Node.js.
Les exports nommés te permettent d'exporter plusieurs valeurs depuis un module. Chaque export a un nom spécifique qui doit être utilisé lors de l'import.
Exporter — utilise le mot-clé export avant une déclaration :
// math.js
export const PI = 3.14159;
export function add(a, b) { return a + b; }
export class Calculator { /* ... */ }Ou exporte plusieurs valeurs en une fois en utilisant une liste d'exports :
const PI = 3.14159;
function add(a, b) { return a + b; }
export { PI, add };Importer — utilise des accolades avec les noms d'exports exacts :
import { PI, add } from './math.js';
console.log(add(2, 3)); // 5
console.log(PI); // 3.14159Renommer lors de l'import avec as :
import { add as sum } from './math.js';
console.log(sum(2, 3)); // 5Importer tout comme un objet namespace :
import * as math from './math.js';
console.log(math.PI); // 3.14159
console.log(math.add(2, 3)); // 5Un export par défaut est la chose "principale" qu'un module fournit. Chaque module peut avoir au maximum un export par défaut.
Exporter une valeur par défaut :
// greet.js
export default function greet(name) {
return 'Hello, ' + name + '!';
}Ou avec une expression :
// config.js
export default {
theme: 'dark',
language: 'en'
};Importer un export par défaut — pas besoin d'accolades, et tu peux utiliser n'importe quel nom :
import greet from './greet.js';
import myConfig from './config.js';
console.log(greet('World')); // 'Hello, World!'
console.log(myConfig.theme); // 'dark'Combiner exports par défaut et nommés :
// utils.js
export default function main() { /* ... */ }
export function helper() { /* ... */ }
export const VERSION = '1.0';import main, { helper, VERSION } from './utils.js';Les exports par défaut sont couramment utilisés pour l'export principal d'un fichier — une classe, un composant, ou la fonction principale.
Les modules ES ont plusieurs fonctionnalités importantes qui les distinguent des scripts normaux.
Mode strict par défaut — les modules s'exécutent automatiquement en mode strict. Tu n'as pas besoin de 'use strict';.
Portée au niveau du module — les variables déclarées dans un module ont une portée limitée à ce module, elles ne sont pas ajoutées à l'objet global :
// Dans un module
const secret = 'hidden'; // Non accessible depuis d'autres modulesLes imports sont remontés — les instructions import sont déplacées vers le haut lors de l'analyse, peu importe où tu les écris.
Structure statique — les imports et exports sont analysés au moment de la compilation, pas à l'exécution. Cela permet le tree-shaking (suppression du code non utilisé) dans les bundlers.
Import dynamique — pour les cas où tu as besoin de charger un module conditionnellement ou à la demande, utilise import() comme une fonction. Elle retourne une Promise :
const button = document.getElementById('load');
button.addEventListener('click', async () => {
const module = await import('./heavy-feature.js');
module.init();
});Les imports dynamiques sont utiles pour le code splitting — charger des parties de ton application uniquement quand elles sont nécessaires, améliorant le temps de chargement initial.
Dans le navigateur, utilise <script type="module"> pour charger les modules ES :
<script type="module" src="app.js"></script>// ========== Exports nommés ==========
// Export en ligne
export const name = 'Kodojo';
export function greet(who) { return `Hello, ${who}!`; }
// Liste d'exports
const a = 1;
const b = 2;
export { a, b };
// Renommer lors de l'export
export { a as alpha, b as beta };
// ========== Export par défaut ==========
export default class App {
constructor() { this.name = 'Kodojo'; }
}
// ========== Imports ==========
// Imports nommés
import { name, greet } from './module.js';
// Renommer lors de l'import
import { name as appName } from './module.js';
// Import namespace
import * as mod from './module.js';
// Import par défaut
import App from './module.js';
// Défaut + nommés
import App, { name, greet } from './module.js';
// Import dynamique (retourne une Promise)
const mod2 = await import('./module.js');Combien d'exports par défaut un module peut-il avoir ?