Une application charge souvent du code que la plupart des visiteurs n'exécuteront jamais : l'éditeur de texte riche, le générateur de PDF, la vue d'administration. Tout cela pèse au premier affichage, pour rien.
La forme import() permet de repousser ce chargement jusqu'à l'instant précis où le code devient nécessaire.
Définition
L'import dynamique est un appel de fonction, import(chemin), qui charge un module pendant l'exécution et renvoie une Promise. Cette promesse est tenue avec un objet contenant tous les exports du module, l'export par défaut compris sous la clé default.
// panier.js
export const TVA = 0.2;
export default function totalTTC(ht) {
return ht * (1 + TVA);
}
// app.js
const panier = await import('./panier.js');
console.log(Object.keys(panier)); // [ 'TVA', 'default' ]
console.log(panier.default(100)); // 120La Destructuration rend l'appel plus lisible : const { default: totalTTC, TVA } = await import('./panier.js').
Ce que le chargement à la demande change
Un Bundler (empaqueteur) traite chaque import() comme une frontière : le module et ses dépendances partent dans un fichier séparé, téléchargé seulement quand la ligne s'exécute. C'est le découpage en morceaux, et c'est souvent le gain de performance le plus rentable d'un projet.
bouton.addEventListener('click', async () => {
const { ouvrirEditeur } = await import('./editeur.js');
ouvrirEditeur();
});Le même mécanisme sert à charger un fichier de traduction selon la langue choisie, ou une bibliothèque de graphiques uniquement sur la page qui en affiche.
Contrairement à un import classique, import() s'écrit n'importe où : dans une condition, dans une fonction, dans un gestionnaire d'événement. C'est justement ce que la forme statique interdit.
Trois pièges à connaître
- Un chemin entièrement variable casse l'analyse. Les outils ont besoin d'en deviner la forme : préférez
import(`./langues/${code}.js`), dont la partie fixe reste lisible, à un chemin construit de bout en bout. - La promesse peut être rompue. Un fichier absent ou une coupure réseau provoquent un rejet, à traiter avec try et catch sous peine d'interface figée.
- Le module reste en cache. Dix appels sur le même chemin ne téléchargent le fichier qu'une fois, inutile de mémoriser le résultat vous-même.
Questions fréquentes
Faut-il toujours écrire await devant import() ?
Non, c'est simplement la forme la plus lisible. import('./x.js').then(...) fait exactement le même travail, et convient quand le code environnant n'est pas une fonction async. Dans un module ES, await est également accepté au niveau supérieur du fichier.
Peut-on l'utiliser dans un fichier CommonJS ?
Oui, et c'est même le pont officiel entre les deux mondes : import() fonctionne dans un fichier CommonJS et sait charger un Module ES, là où require() échoue. La contrepartie est que le résultat arrive de façon asynchrone.
Quand ce découpage devient-il contre-productif ?
Quand les morceaux sont trop petits ou trop nombreux : chaque fichier séparé coûte une requête, et découper un module de deux kilo-octets ne rapporte rien. Réservez la technique aux blocs vraiment lourds ou vraiment rares, et mesurez avant de généraliser.