Vous avez probablement utilisé des modules ES dans le développement JavaScript moderne, mais connaissez-vous l'histoire de leur évolution ? Comprendre le parcours depuis les premières pratiques JavaScript jusqu'au système de modules actuel vous aidera à comprendre le chemin parcouru et pourquoi les modules ES changent la donne.
Nous sommes en 1995, quatre ans après la création de la première page Web. La plupart des sites Web étaient simples : des pages statiques avec du texte et une interactivité minimale. Cependant, les développeurs cherchaient rapidement des moyens de rendre les pages Web plus dynamiques.
Dans cet environnement, Netscape (le navigateur Web dominant à l'époque) a embauché Brendan Eich pour créer un langage de script qui s'exécuterait directement dans le navigateur. Cela a conduit à la naissance de JavaScript, un langage conçu pour être simple et accessible, notamment pour les non-programmeurs comme les concepteurs de sites Web. Et c’est ce qu’il a fait, complétant la première version en 10 jours.
À l'origine, JavaScript était destiné à ajouter de petites améliorations aux pages Web, comme la validation de formulaires, sans avoir besoin d'acheminer les données vers le serveur. Cependant, à mesure que les sites Web devenaient plus interactifs, JavaScript a rapidement dépassé son objectif initial.
Au début, tout le code JavaScript vivait dans une portée mondiale. À mesure que de plus en plus de développeurs ajoutaient du code à la même page, le risque de collisions de noms augmentait. Si plusieurs scripts utilisaient la même variable ou le même nom de fonction, le code pourrait être interrompu de manière inattendue.
Pour gérer cela, les développeurs ont eu recours à des conventions de dénomination pour éviter les collisions. Si un morceau de code n'était destiné à s'exécuter qu'une seule fois, les développeurs l'encapsulaient souvent dans une IIFE (Immediately Invoked Function Expression). Cela a permis de conserver les fonctions et les variables dans la fonction, les empêchant de polluer l'espace de noms global.
(function init() { function getData(){ // ... } })()
C'était suffisant à l'époque car la plupart des sites Web étaient rendus côté serveur avec très peu de logique côté client.
En 2008, Ryan Dahl a créé Node.js, un environnement d'exécution JavaScript pour créer des applications côté serveur. Cela a ouvert un tout nouveau monde de possibilités, mais l'absence de système de modules signifiait que les développeurs avaient du mal à gérer de grandes bases de code.
En 2009, CommonJS a été introduit pour résoudre ce problème côté serveur. Le système de modules CommonJS permettait aux développeurs de définir des modules, d'exposer des fonctionnalités et d'importer d'autres modules. Voici un exemple de la façon dont cela a fonctionné :
const math = require("./math"); function subtract(a,b) { return math.add(a,-b); } module.exports = { subtract: subtract }
Avec CommonJS, chaque fichier est traité comme son propre module, et les modules sont importés à l'aide de la fonction require et exportés à l'aide de module.exports.
Certaines fonctionnalités clés de CommonJS incluent :
L'extension de fichier est facultative lorsqu'un module est requis (par exemple, require('./math') recherche automatiquement math.js).
Le chargement du module est synchrone, ce qui signifie que le programme attend que le module se charge avant de poursuivre l'exécution.
Plus loin dans l'article, nous verrons pourquoi Ryan Dahl a admis regretter ces 2 décisions de conception.
À peu près à la même époque, un autre système de modules appelé AMD (Asynchronous Module Definition) a été développé. Alors que CommonJS se concentrait principalement sur le JavaScript côté serveur, AMD a été conçu pour gérer le JavaScript côté client dans le navigateur.
La principale caractéristique d'AMD était sa capacité à charger des modules de manière asynchrone. Cela a permis au navigateur de charger uniquement le JavaScript nécessaire à une page à un moment donné, améliorant ainsi les performances en réduisant le temps de chargement initial de la page. Il a également résolu les problèmes liés à la résolution des dépendances, garantissant qu'un module ne s'exécuterait qu'une fois le chargement de ses dépendances terminé.
Les avantages d'AMD inclus :
Avec l'essor de npm en 2010 (un gestionnaire de packages pour JavaScript côté serveur), la nécessité de partager du code entre le navigateur et le serveur est devenue évidente. Entrez Browserify, un outil qui permettait aux développeurs d'utiliser les modules CommonJS dans le navigateur en transformant le code pour qu'il soit compatible avec l'environnement du navigateur.
Avec les 2 standards concurrents, CommonJS et AMD. Il y avait un besoin pour un système à module unique qui pourrait fonctionner partout sans avoir besoin d’une étape de construction. Et en 2011, la définition universelle du module (UMD) a été introduite.
UMD a combiné le meilleur des deux mondes, permettant aux développeurs d'écrire un module qui pourrait s'exécuter dans :
UMD est devenu très populaire parmi les auteurs de bibliothèques avec des bibliothèques notables comme Lodash, Underscore.js, Backbone.js et Moment.js qui l'adoptent. Cependant, l’UMD présentait des inconvénients importants. Bien que cela ait résolu le problème de compatibilité, cela s'est accompagné de la complexité de la gestion des deux systèmes et a hérité des problèmes d'AMD et de CommonJS.
En 2015, les modules ES (ESM) ont été introduits dans le cadre du standard ECMAScript, offrant enfin un système de modules natifs pour JavaScript. En 2017, tous les principaux navigateurs prenaient en charge les modules ES, et en 2020, Node.js a également ajouté la prise en charge.
Voyons pourquoi les modules ES sont les meilleurs :
Le code UMD suivant :
(function init() { function getData(){ // ... } })()
peut désormais être réduit à :
(function init() { function getData(){ // ... } })()
Pour être honnête, personne n’a réellement écrit UMD comme ça. Ils ont utilisé des outils comme umdify pour générer ce code. Mais avec les modules ES intégrés, nous pouvons ignorer l'étape de construction et avoir une taille de bundle plus petite.
Les modules ES sont statiques, ce qui signifie que les outils peuvent analyser la structure du code au moment de la compilation pour déterminer quel code est utilisé et lequel ne l'est pas. Cela permet de secouer l'arbre, où le code inutilisé est supprimé du bundle final.
Étant donné que les modules CommonJS et AMD sont dynamiques (évalués au moment de l'exécution), l'arborescence est beaucoup moins efficace avec ces systèmes, ce qui entraîne souvent des bundles plus volumineux.
Lors de l'importation d'un module avec CommonJS, la spécification de l'extension du fichier est facultative.
const math = require("./math"); function subtract(a,b) { return math.add(a,-b); } module.exports = { subtract: subtract }
Mais à quoi font réellement référence les mathématiques ? Est-ce un fichier JavaScript ? Un fichier JSON ? Un fichier index.js dans un répertoire mathématique ?
Lorsque vous utilisez des outils d'analyse statique comme ES Lint, dactylographié ou plus joli, chaque exigence devient un jeu de devinettes.
est-ce math.js ?
est-ce math.jsx ?
est-ce math.cjs ?
c'est math.mjs ?
c'est math.ts ?
c'est math.tsx ?
c'est math.mts ?
c'est math.cts ?
est-ce math/index.js ?
est-ce math/index.jsx ?
Vous voyez l'idée.
La lecture d'un fichier coûte cher. C'est beaucoup moins performant que la lecture depuis la mémoire. L'importation de math/index.js entraîne 9 opérations d'E/S au lieu de 1 ! Et ce jeu de devinettes ralentit nos outils et nuit à l'expérience des développeurs.
Dans les modules ES, nous évitons ce gâchis en rendant obligatoires les extensions de fichiers.
Contrairement à CommonJS, qui charge les modules de manière synchrone (bloquant tout le processus jusqu'à ce que le module soit chargé), les modules ES sont asynchrones. Cela permet à JavaScript de continuer à s'exécuter pendant que le module est chargé en arrière-plan, améliorant ainsi les performances, en particulier dans des environnements comme Node.js.
Malgré les avantages évidents, l'adoption des modules ES n'a pas été une tâche simple. Voici pourquoi la transition a pris si longtemps :
Passer des modules CommonJS aux modules ES n'était pas un changement anodin, surtout pour les grands projets. Les différences de syntaxe, combinées à la nécessité de prendre en charge des outils, ont fait de la migration un effort important.
Il a fallu 5 ans à node.js pour prendre entièrement en charge les modules ES. Pendant ce temps, les développeurs devaient maintenir la compatibilité avec les modules CommonJS (sur le serveur) et ES (sur le navigateur). Ce double support a créé beaucoup de frictions dans l'écosystème.
Même après que Node.js ait ajouté la prise en charge des modules ES, les modules CommonJS n'ont pas pu charger les modules ES. Même si les modules ES pouvaient charger des modules CommonJS, les deux systèmes n'étaient pas totalement interopérables, ce qui créait des maux de tête supplémentaires pour les auteurs de packages qui devaient prendre en charge les deux systèmes.
L'avenir des modules JavaScript est prometteur, et voici quelques développements clés qui feront des modules ES le système dominant à l'avenir :
Dans Node.js 23, nous avons enfin la possibilité de charger des modules ES depuis CommonJS.
Il y a une petite mise en garde : le module ES qui utilise wait de niveau supérieur ne peut pas être importé dans CommonJS, car wait ne peut être utilisé que dans des fonctions asynchrones et CommonJS est synchrone.
Un nouveau registre de packages javascript qui concurrence npm. Il présente de nombreux avantages par rapport à npm que je n'aborderai pas ici. Mais ce qui est intéressant, c’est que vous n’êtes autorisé à télécharger que des packages de modules ES. Pas besoin de supporter les anciennes normes.
Le passage des hacks de portée mondiale aux modules ES modernes a transformé la façon dont nous structurons JavaScript. Après des années d'expérimentation avec CommonJS, AMD et UMD, les modules ES sont devenus la norme claire, offrant une syntaxe plus simple, une meilleure optimisation et des performances améliorées.
Bien que la migration vers les modules ES ait été un défi, notamment avec la prise en charge de Node.js et la compatibilité de l'écosystème, les avantages sont indéniables. Avec Node.js 23 améliorant l'interopérabilité et de nouveaux outils comme JSR promouvant un système de modules unifié, les modules ES sont sur le point de devenir la valeur par défaut pour JavaScript.
Alors que nous continuons à adopter les modules ES, nous pouvons nous attendre à un code plus propre, plus rapide et plus maintenable, marquant une nouvelle ère de modularité dans le développement JavaScript.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!