JavaScript export et import
Apprenez export et import en JavaScript : exports nommés, exports par défaut, renommage avec as, imports de namespace, importation dynamique, réexportation et pièges courants — avec des exemples exécutables.
En JavaScript moderne, les mots-clés export et import permettent de diviser un programme en plusieurs fichiers et de les réassembler. Un fichier qui les utilise est un module ES : au lieu de tout entasser dans un seul script géant, vous conservez chaque fonctionnalité dans son propre fichier, déclarez ce qu'il partage avec l'extérieur (export), et importez uniquement ce dont vous avez besoin ailleurs (import).
Ce guide couvre toutes les formes que vous rencontrerez en pratique — exports nommés, exports par défaut, renommage avec as, imports de namespace, réexportation et import() dynamique — ainsi que les pièges qui font trébucher les développeurs. Chaque exemple est petit et exécutable.
Pourquoi des modules ?
Avant les modules ES, partager du code signifiait attacher des éléments à l'objet global en espérant que les noms n'entrent pas en collision. Les modules règlent ce problème :
- Encapsulation — tout ce que vous n'exportez pas reste privé au fichier.
- Dépendances explicites — les lignes
importen haut d'un fichier constituent une liste précise de ce dont il dépend. - Évaluation unique — le code de niveau supérieur d'un module s'exécute une seule fois, quel que soit le nombre de fichiers qui l'importent ; le résultat est mis en cache et partagé.
Pour exécuter des modules dans le navigateur, chargez votre fichier d'entrée avec type="module" :
<script src="app.js" type="module"></script>Avec Node.js, nommez vos fichiers .mjs ou définissez "type": "module" dans package.json.
Exports nommés
Un export nommé expose un binding sous son propre nom. Un module peut avoir autant d'exports nommés que vous le souhaitez.
// math.js
export const PI = 3.14159;
export function add(a, b) {
return a + b;
}
// You can also list exports at the bottom:
const subtract = (a, b) => a - b;
export { subtract };Vous importez les exports nommés entre accolades, en utilisant exactement les mêmes noms :
// app.js
import { add, PI } from './math.js';
console.log(add(2, 3)); // 5
console.log(PI); // 3.14159Les noms doivent correspondre à l'export. import { Add } alors que l'export s'appelle add lève une SyntaxError — il n'existe pas d'export nommé ainsi.
Renommage avec as
Lorsque deux modules exportent le même nom, ou qu'un nom est maladroit, renommez-le à l'import (ou à l'export) avec as :
// Rename on import
import { add as sum } from './math.js';
console.log(sum(1, 1)); // 2// Rename on export
const internalAdd = (a, b) => a + b;
export { internalAdd as add };Exports par défaut
Un export par défaut est l'élément « principal » qu'un module fournit. Un module ne peut avoir qu'un seul export par défaut.
// multiply.js
export default function multiply(a, b) {
return a * b;
}Lors de l'import d'un export par défaut, vous n'utilisez pas d'accolades, et vous choisissez vous-même le nom local :
// app.js
import multiply from './multiply.js'; // any name works
console.log(multiply(4, 5)); // 20Comme l'importateur le nomme lui-même, les exports par défaut sont souvent utilisés lorsqu'un fichier représente une seule chose — une seule classe, un seul composant React, un seul objet de configuration.
Combiner export par défaut et exports nommés
Un module peut avoir les deux. L'export par défaut se place en dehors des accolades, les exports nommés à l'intérieur :
// user.js
export default class User { /* ... */ }
export const ADMIN_ROLE = 'admin';// app.js
import User, { ADMIN_ROLE } from './user.js';Privilégiez les exports nommés par défaut. Ils rendent les imports auto-documentés et permettent aux outils (autocomplétion, « trouver les références », tree-shaking) de fonctionner de manière fiable. Recourez à un export par défaut uniquement lorsqu'un fichier exporte vraiment une seule chose.
Techniques d'importation
Import de namespace (import * as)
Pour récupérer tout ce qu'un module exporte sous forme d'un seul objet, utilisez un import de namespace :
// app.js
import * as math from './math.js';
console.log(math.add(5, 3)); // 8
console.log(math.PI); // 3.14159Chaque export nommé devient une propriété de math. Un export par défaut, s'il existe, apparaît sous math.default. Utilisez cette syntaxe lorsqu'un module regroupe de nombreux utilitaires liés.
Import dynamique (import())
L'instruction import ci-dessus est statique — elle s'exécute avant tout le reste et son chemin doit être un littéral de chaîne. Parfois, vous voulez charger un module à la demande : uniquement lorsque l'utilisateur ouvre une fonctionnalité, ou en fonction d'une condition d'exécution. Pour cela, utilisez import() comme une fonction. Elle retourne une Promise qui se résout en l'objet namespace du module :
// Load a heavy module only when needed
async function openEditor() {
const editor = await import('./editor.js');
editor.init();
}L'import dynamique fonctionne à l'intérieur des fonctions et des conditions — des endroits où un import statique n'est pas autorisé — ce qui en fait la base du code-splitting et du chargement différé.
Réexportation
Un fichier « barrel » peut rassembler des exports de plusieurs modules et les retransmettre, afin que les consommateurs importent depuis un seul endroit :
// shapes/index.js
export { Circle } from './circle.js';
export { Square } from './square.js';
export { default as Triangle } from './triangle.js'; // re-export a default as a name// app.js
import { Circle, Square, Triangle } from './shapes/index.js';Pièges courants
- Les extensions comptent dans le navigateur et dans Node ESM.
import { x } from './math'échoue ; écrivez'./math.js'. import/exportne fonctionnent qu'au niveau supérieur d'un module — pas à l'intérieur d'un blocifou d'une fonction. Utilisezimport()dynamique pour le chargement conditionnel.- Les imports sont des bindings en lecture seule. Vous pouvez lire une valeur importée mais pas la réassigner ;
add = 5sur un import lève une erreur. - Un export par défaut n'est pas magiquement nommé.
export default addexporte la valeur, pas le nomadd; l'importateur choisit le nom local. - Les modules sont différés et s'exécutent automatiquement en mode strict — pas besoin de
'use strict'.
Un exemple concret : un mini système de bibliothèque
Plusieurs modules exportent chacun un array d'objects livres, et un fichier principal les importe et les combine.
index.html :
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Interactive Library App</title>
</head>
<body>
<h1>Interactive Library App</h1>
<button id="loadFiction">Load Fiction</button>
<button id="loadSciFi">Load Sci-Fi</button>
<div id="bookList"></div>
<script src="app.js" type="module"></script>
</body>
</html>fiction.js :
export const fictionBooks = [
{ title: 'Pride and Prejudice', author: 'Jane Austen' },
{ title: 'To Kill a Mockingbird', author: 'Harper Lee' }
];sciFi.js :
export const sciFiBooks = [
{ title: 'Dune', author: 'Frank Herbert' },
{ title: 'Neuromancer', author: 'William Gibson' }
];app.js :
import { fictionBooks } from './fiction.js';
import { sciFiBooks } from './sciFi.js';
function displayBooks(books) {
const list = document.getElementById('bookList');
list.innerHTML = '';
books.forEach(book => {
const item = document.createElement('div');
item.textContent = `${book.title} by ${book.author}`;
list.appendChild(item);
});
}
document
.getElementById('loadFiction')
.addEventListener('click', () => displayBooks(fictionBooks));
document
.getElementById('loadSciFi')
.addEventListener('click', () => displayBooks(sciFiBooks));Chaque catégorie vit dans son propre module et exporte une function ou des données, et app.js les réunit. Cette séparation des données et du comportement maintient chaque fichier compact et facilite l'ajout de nouvelles catégories.
Bonnes pratiques
- Privilégiez les exports nommés pour que les imports se documentent d'eux-mêmes et que le tree-shaking fonctionne.
- Une responsabilité par module — un fichier doit être facile à décrire en une phrase.
- Utilisez les fichiers barrel avec parcimonie — ils ordonnent les imports, mais peuvent nuire au tree-shaking s'ils sont trop nombreux.
- Chargez en différé le code lourd ou rarement utilisé avec
import()dynamique. - Incluez toujours les extensions de fichier dans les chemins relatifs.
Conclusion
Les exports nommés vous donnent plusieurs bindings par fichier, les exports par défaut vous donnent un seul export « principal », et import (avec as, * as et import() dynamique) vous offre un contrôle précis sur ce qui entre et quand. Ensemble, ils transforment un tas de scripts en un graphe de modules maintenable et optimisable par tree-shaking. Découvrez ensuite comment les modules sont chargés dans le navigateur dans Modules, introduction.