| [ Web Proxy ] |
| Viewing: https://developer.mozilla.org/fr/docs/Web/JavaScript/Guide/Modules | [Back] [Original] |
Get to know MDN better
Cette page a t traduite partir de l'anglais par la communaut. Vous pouvez contribuer en rejoignant la communaut francophone sur MDN Web Docs.
Ce guide aborde l'ensemble des notions vous permettant d'utiliser la syntaxe des modules en JavaScript.
.mjs ou .js
Les programmes JavaScript ont commenc par tre assez petits, ralisant des tches isoles uniquement l o l'interactivit tait ncessaire. Aprs plusieurs annes, nous avons maintenant des applications compltes qui sont excutes dans les navigateurs avec des codes complexes et volumineux. Des programmes JavaScript sont galement excuts dans d'autres contextes (Node.js par exemple).
Il a donc t question de fournir un mcanisme pour diviser les programmes JavaScript en plusieurs modules qu'on pourrait importer les uns dans les autres. Cette fonctionnalit tait prsente dans Node.js depuis longtemps et plusieurs bibliothques et frameworks JavaScript ont permis l'utilisation de modules (CommonJS, AMD, RequireJS ou, plus rcemment, Webpack et Babel).
Bonne nouvelle, les navigateurs ont galement commenc prendre en charge ces fonctionnalits nativement. C'est le sujet de ce guide.
Cette implmentation permettra aux navigateurs d'optimiser le chargement des modules, rendant le fonctionnement plus efficace qu'une bibliothque tierce avec un traitement ct client des allers-retours sur le rseau.
L'utilisation des modules natifs JavaScript repose sur les instructions import et export dont vous pouvez voir l'tat de la compatibilit ici :
Pour illustrer le fonctionnement des modules, nous avons cr un ensemble d'exemples disponibles sur GitHub. Ces exemples illustrent un ensemble de modules pour crer un lment <canvas> sur une page web puis dessiner (et afficher des informations) sur les diffrentes formes du canevas.
Ces oprations sont assez simples, mais nous les avons choisies pour nous concentrer plutt sur le fonctionnement des modules.
Note : Si vous souhaitez tlcharger les exemples et les excuter en local, vous devrez utiliser un serveur web local.
Dans notre premier exemple (cf. basic-modules), nous avons l'arborescence de fichier suivante :
index.html
main.js
modules/
canvas.js
square.js
Note : Tous les exemples de ce guide suivent la mme structure.
Le rpertoire ddi aux modules contient deux modules :
canvas.js responsable de fonctions pour grer le canevas
create() cre un canevas avec les dimensions souhaites (width / height) l'intrieur d'un lment <div> dot d'un identifiant et qui est ajout l'intrieur d'un lment indiqu. Cette fonction renvoie l'objet contenant le contexte du canevas et l'identifiant du conteneur.createReportList() cre une liste non ordonne l'intrieur d'un lment indiqu et dans lequel on affiche des donnes. Cette fonction renvoie l'identifiant de la liste.square.js
name une constante qui est une chane de caractres : "square".draw() dessine un carr avec une taille/position/couleur donne sur le canevas indiqu. Cette fonction renvoie un objet contenant la taille du carr, sa position et sa couleur.reportArea() crit la surface d'un carr dans une liste donne en fonction de la longueur de son ct.reportPerimeter() crit le primtre d'un carr dans une liste donne en fonction de la longueur de son ct..mjs ou .jsTout au long de cet article, nous utiliserons l'extension .js pour les fichiers de nos modules. Toutefois, dans d'autres ressources, vous pourrez voir l'extension .mjs utilise. Par exemple dans les recommandations de la documentation de V8. Les raisons alors fournies sont :
Toutefois, nous avons dcid de conserver l'extension .js. Pour que les modules fonctionnent dans les navigateurs, vous devez vous assurer qu'ils sont servis avec un en-tte Content-Type qui contient un type MIME JavaScript comme text/javascript. Si ce n'est pas le cas, vous aurez une erreur de vrification du type MIME et le navigateur n'excutera pas le JavaScript de ces fichiers. La plupart des serveurs web utilisent le type MIME correct pour les fichiers .js mais pas encore pour les fichiers .mjs. Parmi les serveurs qui servent correctement les fichiers .mjs, on trouve GitHub Pages et http-server en Node.js.
Il n'y a pas de problme si vous utilisez dj un tel environnement ou si vous savez ce que vous faites et avez accs la configuration de votre serveur pour paramtrer le bon Content-Type pour les fichiers .mjs). Cela pourrait malheureusement tre source de confusion si vous ne contrlez pas le serveur qui sert les fichiers ou que vous publiez des fichiers pour un usage public (comme nous le faisons ici).
Aussi, des fins d'apprentissage et de portabilit, nous avons dcid de conserver l'extension .js ici.
Si la distinction apporte par l'usage de .mjs pour identifier les modules (vs .js pour les fichiers JavaScript normaux) vous est utile, vous pouvez toujours utiliser l'extension .mjs pour le dveloppement et convertir cette extension en .js lors de la compilation.
On notera que :
.mjs.<script type="module"> est utilis pour indiquer quand on cible un module.Pour commencer et afin d'utiliser les fonctionnalits d'un module, on devra les exporter. Pour cela, on utilisera l'instruction export.
La mthode la plus simple consiste placer cette instruction devant chaque valeur qu'on souhaite exporter, par exemple :
export const name = "square";
export function draw(ctx, length, x, y, color) {
ctx.fillStyle = color;
ctx.fillRect(x, y, length, length);
return {
length: length,
x: x,
y: y,
color: color,
};
}
Il est possible d'exporter des fonctions, des variables (qu'elles soient dfinies avec var, let ou const) et aussi des classes (que nous verrons par la suite). Les valeurs exportes doivent tre prsentes au plus haut niveau du script, il n'est pas possible d'utiliser export dans une fonction.
Une mthode plus concise consiste exporter l'ensemble des valeurs grce une seule instruction situe la fin du fichier : les valeurs sont spares par des virgules et la liste est dlimite entre accolades :
export { name, draw, reportArea, reportPerimeter };
Lorsque des fonctionnalits sont exportes par un premier module, on peut les importer dans un script afin de les utiliser. Voici la mthode la plus simple pour ce faire :
import { name, draw, reportArea, reportPerimeter } from "./modules/square.js";
On utilise ici l'instruction import, suivie d'une liste d'identifiants spars par des virgules et dlimite par des accolades, suivie du mot-cl from puis du chemin vers le fichier du module. Le chemin est relatif la racine du site. Dans notre cas, pour basic-module, on crira /js-examples/module-examples/basic-modules.
Ici, nous avons crit le chemin d'une faon lgrement diffrente : on utilise le point (.) afin d'indiquer l'emplacement courant , suivi du chemin vers le fichier. Cela permet d'viter d'avoir crire l'intgralit du chemin chaque fois, c'est aussi plus court et cela permet de dplacer le script et le modules sans avoir modifier les scripts.
Ainsi :
/js-examples/module-examples/basic-modules/modules/square.js
devient :
./modules/square.js
Vous pouvez voir ces lignes dans main.js.
Note :
Pour certains systmes de module, on peut omettre l'extension de fichier et le point (c'est--dire qu'on peut crire '/modules/square'). Cela ne fonctionne pas pour les modules JavaScript !
Une fois les fonctionnalits importes dans le script, vous pouvez utiliser les valeurs dans votre script. Dans main.js, aprs les lignes d'import, on trouvera :
let myCanvas = create("myCanvas", document.body, 480, 320);
let reportList = createReportList(myCanvas.id);
let square1 = draw(myCanvas.ctx, 50, 50, 100, "blue");
reportArea(square1.length, reportList);
reportPerimeter(square1.length, reportList);
Note :
Bien que les fonctionnalits importes soient disponibles dans le fichier, ce ne sont que des vues en lecture seule pour les fonctionnalits exportes. On ne peut pas changer la variable importe, mais on peut toujours modifier ses proprits ( la faon dont les variables sont gres avec const). De plus, ces fonctionnalits sont importes avec des liaisons dynamiques, ce qui signifie que leur valeur peut changer, mme si on ne peut pas modifier la liaison ( la diffrence de const).
Il faut ensuite pouvoir charger le script main.js sur la page HTML. Pour cela, nous allons voir qu'il y a quelques diffrences avec le chargement d'un script classique .
Tout d'abord, il est ncessaire d'indiquer type="module" dans l'lment <script> afin d'indiquer qu'on charge des modules :
<script type="module" src="main.js"></script>
On peut aussi embarquer directement le script du module dans le fichier HTML en plaant le code JavaScript dans le corps de l'lment <script> :
<script type="module">
/* Code du module JavaScript */
</script>
Le script dans lequel on importe les fonctionnalits du module agira comme le script de plus haut niveau. Si ce dernier est absent, Firefox (par exemple) lvera une erreur SyntaxError: import declarations may only appear at top level of a module.
Les instructions import et export ne peuvent tre utilises qu' l'intrieur de modules et pas depuis des scripts classiques.
file://), vous rencontrerez des erreurs CORS pour des raisons de scurit. Il faut donc un serveur local afin de pouvoir tester.defer (voir les attributs de <script>) lors du chargement d'un module, celui-ci tant automatiquement diffr.<script>.Jusqu' prsent, nous avons utilis des exports nomms chaque valeur est exporte avec un nom et c'est ce nom qui est galement utilis lorsqu'on ralise l'import.
Il existe galement un export par dfaut conu pour simplifier l'export d'une fonction par module et pour faciliter l'interoprabilit avec les systmes de module CommonJS et AMD (pour plus d'informations, voir ES6 en dtails : les modules).
Prenons un exemple pour comprendre le fonctionnement des exports par dfaut. Dans square.js, on a une fonction intitule randomSquare() qui cre un carr avec une taille/couleur/position alatoire. On souhaite exporter cette fonction par dfaut et on crit donc ceci la fin du fichier :
export default randomSquare;
On notera ici l'absence d'accolades.
On aurait galement pu ajouter export default devant le mot-cl function et la dfinir comme fonction anonyme :
export default function(ctx) {
...
}
Dans le fichier main.js, on importe la fonction par dfaut avec cette ligne
import randomSquare from "./modules/square.js";
On voit ici aussi l'absence d'accolade, car il n'y a qu'un seul export par dfaut possible par module (et ici, on sait qu'il s'agit de randomSquare). La ligne ci-avant est en fait une notation raccourcie quivalente :
import { default as randomSquare } from "./modules/square.js";
Note : Pour en savoir plus sur le renommage des objets exports, voir ci-aprs Renommage des imports et des exports.
Jusqu' prsent, notre exemple fonctionne. Mais que se passerait-il si nous ajoutions un module permettant de dessiner une autre forme comme un cercle ou un triangle ? Ces formes disposeraient sans doute galement de fonctions telles que draw(), reportArea(), etc. Si on essaie d'importer ces fonctions avec les mmes noms dans le module de plus haut niveau, nous allons avoir des conflits et des erreurs.
Heureusement, il existe diffrentes faons de rsoudre ce problme.
Entre les accolades utilises pour les instructions import et export, on peut utiliser le mot-cl as avec un autre nom afin de modifier le nom par lequel on souhaite identifier la fonctionnalit.
Ainsi, les deux fragments qui suivent permettraient d'obtenir le mme rsultat de faons diffrentes :
// dans module.js
export {
fonction1 as nouveauNomDeFonction,
fonction2 as autreNouveauNomDeFonction,
};
// dans main.js
import {
nouveauNomDeFonction,
autreNouveauNomDeFonction,
} from "./modules/module.js";
// dans module.js
export { fonction1, fonction2 };
// dans main.js
import {
fonction1 as nouveauNomDeFonction,
fonction2 as autreNouveauNomDeFonction,
} from "./modules/module.js";
Prenons un exemple concret. Dans le rpertoire renaming, vous verrez le mme systme de modules que prcdemment auquel nous avons ajout circle.js et triangle.js afin de dessiner et d'crire des informations sur des cercles et des triangles.
Dans chaque module, on exporte les fonctionnalits avec des noms identiques : l'instruction export utilise est la mme chaque fin de fichier :
export { name, draw, reportArea, reportPerimeter };
Lorsqu'on importe les valeurs dans main.js, si on essaie d'utiliser
import { name, draw, reportArea, reportPerimeter } from "./modules/square.js";
import { name, draw, reportArea, reportPerimeter } from "./modules/circle.js";
import { name, draw, reportArea, reportPerimeter } from "./modules/triangle.js";
Le navigateur dclenchera une erreur telle que SyntaxError: redeclaration of import name (Firefox).
Pour viter ce problme, on renomme les imports afin qu'ils soient uniques :
import {
name as squareName,
draw as drawSquare,
reportArea as reportSquareArea,
reportPerimeter as reportSquarePerimeter,
} from "./modules/square.js";
import {
name as circleName,
draw as drawCircle,
reportArea as reportCircleArea,
reportPerimeter as reportCirclePerimeter,
} from "./modules/circle.js";
import {
name as triangleName,
draw as drawTriangle,
reportArea as reportTriangleArea,
reportPerimeter as reportTrianglePerimeter,
} from "./modules/triangle.js";
On aurait pu galement rsoudre le problme dans les fichiers de chaque module.
// dans square.js
export {
name as squareName,
draw as drawSquare,
reportArea as reportSquareArea,
reportPerimeter as reportSquarePerimeter,
};
// dans main.js
import {
squareName,
drawSquare,
reportSquareArea,
reportSquarePerimeter,
} from "./modules/square.js";
Les deux approches fonctionnent. C'est vous de choisir le style. Toutefois, il est souvent plus pratique d'effectuer le renommage l'import, notamment lorsqu'on importe des fonctionnalits de modules tiers sur lesquels on n'a pas le contrle.
La mthode prcdente fonctionne mais reste brouillonne . Pour faire mieux, on peut importer l'ensemble des fonctionnalits de chaque module dans un objet, de la faon suivante :
import * as Module from "./modules/module.js";
Cela rcupre tous les exports disponibles depuis module.js et les transforme en proprits et mthodes rattaches l'objet Module qui fournit alors un espace de noms (namespace) :
Module.function1()
Module.function2()
etc.
L encore, prenons un exemple concret avec le rpertoire module-objects. Il s'agit du mme exemple que prcdemment mais qui a t rcrit afin de tirer parti de cette syntaxe. Dans les modules, les exports sont tous crits ainsi :
export { name, draw, reportArea, reportPerimeter };
En revanche, pour les imports, on les rcupre ainsi :
import * as Canvas from "./modules/canvas.js";
import * as Square from "./modules/square.js";
import * as Circle from "./modules/circle.js";
import * as Triangle from "./modules/triangle.js";
Dans chaque cas, on peut accder aux imports comme proprits des objets ainsi crs :
let square1 = Square.draw(myCanvas.ctx, 50, 50, 100, "blue");
Square.reportArea(square1.length, reportList);
Square.reportPerimeter(square1.length, reportList);
On obtient alors un code plus lisible.
Comme mentionn avant, il est possible d'importer et d'exporter des classes. Cette mthode peut aussi tre utilise afin d'viter les conflits de nommage. Elle s'avre notamment utile lorsque vous utilisez dj des classes pour construire vos objets (cela permet de garder une certaine cohrence dans le style).
Pour voir le rsultat obtenu, vous pouvez consulter le rpertoire classes du dpt o l'ensemble a t rcrit pour tirer parti des classes ECMAScript. Ainsi, square.js contient dsormais l'ensemble des fonctionnalits via une classe :
class Square {
constructor(ctx, listId, length, x, y, color) {
...
}
draw() {
...
}
...
}
Il suffit d'exporter cette classe :
export { Square };
Puis de l'importer ainsi dans main.js :
import { Square } from "./modules/square.js";
Ensuite, on peut utiliser cette classe afin de dessiner le carr :
let square1 = new Square(myCanvas.ctx, myCanvas.listId, 50, 50, 100, "blue");
square1.draw();
square1.reportArea();
square1.reportPerimeter();
Il arrivera qu'on veuille agrger des modules entre eux. On peut avoir plusieurs niveaux de dpendances et vouloir simplifier les choses en combinant diffrents sous-modules en un seul module parent. Pour cela, on pourra utiliser la notation raccourcie suivante :
export * from "x.js";
export { name } from "x.js";
Pour voir cela en pratique, vous pouvez consulter le rpertoire module-aggregation. Dans cet exemple (construit sur le prcdent qui utilise les classes), on a un module supplmentaire intitul shapes.js qui agrge les fonctionnalits fournies par circle.js, square.js et triangle.js. Les sous-modules ont galement t dplacs dans un rpertoire shapes situ dans un rpertoire modules. L'arborescence utilise est donc :
modules/
canvas.js
shapes.js
shapes/
circle.js
square.js
triangle.js
Dans chaque sous-module, l'export aura la mme forme :
export { Square };
Pour l'agrgation au sein de shapes.js, on crit les lignes suivantes :
export { Square } from "./shapes/square.js";
export { Triangle } from "./shapes/triangle.js";
export { Circle } from "./shapes/circle.js";
On rcupre ainsi l'ensemble des exports de chaque module et on les rend disponibles via shapes.js.
Note :
Cette notation ne permet que de rediriger les exports via le fichier. Les objets imports/exports n'existent pas vraiment dans shapes.js et on ne peut donc pas crire de code utile qui les manipule.
Dans le fichier main.js, on pourra alors remplacer :
import { Square } from "./modules/square.js";
import { Circle } from "./modules/circle.js";
import { Triangle } from "./modules/triangle.js";
par :
import { Square, Circle, Triangle } from "./modules/shapes.js";
Cette nouvelle fonctionnalit permet aux navigateurs de charger les modules lorsqu'ils sont ncessaires plutt que de tout prcharger en avance de phase. Cette mthode offre de nombreux avantages quant aux performances. Voyons comment cela fonctionne.
Pour utiliser cette fonctionnalit, on pourra utiliser import() comme une fonction et lui passer le chemin du module en argument. Cette fonction renverra une promesse, qui sera rsolue en un module objet donnant accs aux exports.
import("./modules/monModule.js").then((module) => {
// Faire qqc avec le module.
});
Dans nos exemples, regardons le rpertoire dynamic-module-imports, galement bas sur les classes. Cette fois, on ne dessine rien au chargement de l'exemple, mais on ajoute trois boutons "Circle", "Square" et "Triangle" qui, lorsqu'ils seront utiliss, chargeront dynamiquement les modules ncessaires et les utiliseront pour charger la forme associe.
Dans cet exemple, nous avons uniquement modifi index.html et main.js les exports restent les mmes.
Dans main.js, on rcupre une rfrence chaque bouton en utilisant document.querySelector(). Par exemple :
let squareBtn = document.querySelector(".square");
Ensuite, on attache un gestionnaire d'vnement chaque bouton afin qu'on puisse appuyer dessus. Le module correspondant est alors charg dynamiquement et utilis pour dessiner la forme :
squareBtn.addEventListener("click", () => {
import("./modules/square.js").then((Module) => {
let square1 = new Module.Square(
myCanvas.ctx,
myCanvas.listId,
50,
50,
100,
"blue",
);
square1.draw();
square1.reportArea();
square1.reportPerimeter();
});
});
On voit ici que, parce que la promesse renvoie un objet module la rsolution, la classe est une proprit de cet objet et qu'il faut ajouter cet espace de nom devant le constructeur export pour l'utiliser. Autrement dit, avec cette mthode, on doit ajouter Module. devant Square (plutt que d'utiliser uniquement Square).
await au plus haut niveauL'utilisation du mot-cl await au plus haut niveau est une fonctionnalit disponible dans les modules. Cela permet d'avoir des modules qui agissent comme des grandes fonctions asynchrones, ce qui signifie que le code peut tre valu avant d'tre utilis dans des modules parents mais sans bloquer le chargement des modules frres et surs.
Prenons un exemple. Vous trouverez les fichiers et le code de cette section au sein du rpertoire top-level-await qui repart des exemples prcdents.
Tout d'abord, on dclare une palette de couleurs dans un fichier colors.json distinct :
{
"yellow": "#F4D03F",
"green": "#52BE80",
"blue": "#5499C7",
"red": "#CD6155",
"orange": "#F39C12"
}
Ensuite, on cre un module intitul getColors.js qui utilise une requte fetch pour charger le fichier colors.json et qui renvoie les donnes comme un objet.
// requte fetch
const colors = fetch("../data/colors.json").then((response) => response.json());
export default await colors;
Voyez ici la dernire ligne qui effectue l'export.
On y utilise le mot-cl await avant la constante colors qu'on souhaite exporter. Cela signifie que si d'autres modules incluent celui-ci, ils devront attendre que colors ait t tlcharg et analys avant de pouvoir l'utiliser.
Incluons ce module dans le fichier main.js :
import colors from './modules/getColors.js';
import { Canvas } from './modules/canvas.js';
let circleBtn = document.querySelector('.circle');
...
Utilisons colors plutt que les chanes de caractres prcdemment utilises pour l'appel aux fonctions de formes :
...
let square1 = new Module.Square(myCanvas.ctx, myCanvas.listId, 50, 50, 100, colors.blue);
...
let circle1 = new Module.Circle(myCanvas.ctx, myCanvas.listId, 75, 200, 100, colors.green);
...
let triangle1 = new Module.Triangle(myCanvas.ctx, myCanvas.listId, 100, 75, 190, colors.yellow);
...
C'est utile ici, car le code au sein de main.js ne s'excutera pas tant que le code de getColors.js n'aura pas t excut. Toutefois, cela ne bloquera pas le chargement des autres modules. Ainsi, notre module canvas.js continuera de charger tandis que les donnes de colors sont rcupres.
Voici quelques notes pour aider comprendre et diagnostiquer les problmes parfois rencontrs avec les modules. N'hsitez pas ajouter vos conseils cette liste si vous en avez.
.js doivent tre chargs avec le type MIME text/javascript (ou avec un autre type MIME compatible JavaScript tel que application/javascript, text/javascript tant recommand), sinon on aura une erreur lors de la vrification du type MIME.file://, on aura des erreurs CORS relatives la scurit. Pour tester les modules, on doit donc mettre en place un serveur (ou, par exemple, utiliser les pages GitHub)..mjs est une extension relativement rcente et certains systmes d'exploitation ne la reconnatront pas et/ou tenteront de la remplacer (ex. macOS pourra silencieusement ajouter un .js aprs le .mjs). Dans ce cas, afficher les extensions de tous les fichiers par dfaut pourra permettre de vrifier.Cette page a t modifie le 4 dc. 2025 par les contributeurices du MDN.
Certaines parties de ce contenu sont protges par le droit d'auteur 19982026 des contributeurs individuels de mozilla.org. Contenu disponible sous une licence Creative Commons.
| Web Proxy Viewer | New URL | Original Page |