Module ES en JavaScript : le système de modules officiel du langage

Un module ES est un fichier qui garde ses variables pour lui et publie ce qu'il partage. Voici ses règles, ses différences avec un script, et comment l'activer.
4 min de lecture
Believemy logo

Deux fichiers JavaScript posés dans une même page partagent tout : chacun voit les variables de l'autre, et une variable total déclarée dans le premier écrase celle du second. Cette absence de frontière a longtemps été la première source de pannes d'un projet un peu gros.

Les modules ES ont réglé le problème en donnant à chaque fichier sa propre portée, et une façon officielle de dire ce qu'il accepte de partager.


Définition

Un module ES est un fichier JavaScript dont les valeurs restent privées tant qu'il ne les publie pas avec export, et que les autres fichiers récupèrent avec import. C'est le système de modules officiel du langage, compris aussi bien par les navigateurs que par Node.js.

JAVASCRIPT
// compteur.js
export let vues = 0;

export function ajouter() {
  vues += 1;
}

// app.js
import { vues, ajouter } from './compteur.js';

console.log(vues); // 0
ajouter();
console.log(vues); // 1

La deuxième lecture affiche 1, ce qui surprend souvent : un import n'est pas une copie de la valeur, c'est un lien permanent vers la variable d'origine.

Ce qu'un module ES résout avant exécution, et ce qui attend l'exécutionDeux frises sur la même échelle. En haut, les imports statiques : les quatre fichiers sont résolus dans une seule attente, avant la première ligne exécutée. En bas, quatre await import() successifs : chaque fichier attend que sa ligne s'exécute, et la frise du bas est quatre fois plus longue que celle du haut.import … fromrésolu avant exécutionle moteur lit tous les imports avant la première lignepanier.jsapi.jsui.jsoutils.js1 attente, les quatre chargés ensembleimport(…)résolu pendant l'exécutionchaque await import() attend que sa ligne s'exécutepanier.jsapi.jsui.jsoutils.js4 attentes, un chargement par ligneLe même graphe : une attente au lieu de quatre


Ce qui sépare un module d'un simple script

  • Une portée à lui : ce qui n'est pas exporté reste invisible de l'extérieur, sans devoir emballer le fichier dans une fonction.
  • Le mode strict, toujours : pas besoin d'écrire "use strict", un module l'applique d'office.
  • Une analyse avant exécution : le moteur lit tous les imports et exports avant de lancer la moindre ligne, ce qui lui permet de charger les dépendances en amont.
  • Une seule évaluation : un module importé par dix fichiers n'est exécuté qu'une fois, et les dix reçoivent le même exemplaire.
  • Un chargement différé : dans le navigateur, un module attend que la page soit analysée, comme le ferait l'attribut defer.
Bon à savoir

Au niveau supérieur d'un module, this vaut undefined et non l'objet global. Du code ancien qui s'appuyait sur cette valeur pour installer une variable globale cesse donc de fonctionner une fois passé en module.


Activer les modules dans un projet

Dans une page web, l'attribut suffit : <script type="module" src="app.js"></script>. Le fichier et tout ce qu'il importe sont alors traités comme des modules.

Côté serveur, deux chemins mènent au même résultat : déclarer "type": "module" dans le fichier package.json, ce qui vaut pour tous les .js du projet, ou nommer le fichier avec l'extension .mjs, qui force le traitement en module quel que soit le reste.


Questions fréquentes

Question

Pourquoi mon module échoue-t-il quand j'ouvre le fichier HTML à la main ?

Parce qu'un double-clic ouvre la page avec l'adresse file://, et que les modules obéissent aux règles de sécurité du chargement réseau, qui rejettent ce protocole. Il faut servir le dossier par un vrai serveur, ne serait-ce que celui d'un outil de développement, et l'erreur disparaît.


Question

Peut-on mélanger modules ES et CommonJS ?

Un module ES peut importer un fichier CommonJS, avec quelques limites sur les exports nommés. L'inverse est plus délicat : un fichier CommonJS ne peut pas toujours réclamer un module ES de façon synchrone, et la solution universelle reste l'import() (import dynamique), qui fonctionne dans les deux sens.


Question

Faut-il un outil de build pour utiliser des modules ES ?

Non : tous les navigateurs actuels les comprennent nativement, et une petite application peut s'en passer complètement. Un Bundler (empaqueteur) redevient utile quand le nombre de fichiers grimpe, car chaque import déclenche une requête réseau, ou quand vous voulez profiter du Tree shaking (élagage) et de la Minification.

Termes connexes

Découvrez notre glossaire JavaScript

Tous les mots de JavaScript expliqués simplement : mots-clés, objets natifs, méthodes, erreurs et concepts. Définitions claires et exemples qui tournent, pour apprendre et pour se dépanner.

Partager cet article

Tu veux nous aider ? Fais un lien vers cet article sur tes réseaux ou encore mieux : sur ton site, dans un article ou dans ta newsletter.