Mardi 16 février 2021

Le moment où Ronan Amicel partage une exploration à propos des modules JavaScript.

Le 16 février à 21:02

Je n’ai pas de livre à recommander, mais il y a quelques mois je me suis moi-même retrouvé à devoir faire (et donc comprendre, on ne se refait pas) plus de choses que je ne l’aurais voulu avec JS. Ces histoires de modules, ce n’était pas clair pour moi : AMD, CommonJS, Bowserify, Node.js, ES6… Donc un matin je me suis posé et j’ai essayé de m’orienter dans ce bazar, et j’ai pris quelques notes.

Au départ, le langage n’avait pas de mécanismes pour découper le code en modules.

On peut découper en plusieurs fichiers, mais tous les scripts chargés dans le navigateur partagent le même espace de nommage global.

Ce qui pose des problèmes : - d’isolation - d’ordre de chargement / dépendances

Pour résoudre le problème d’isolation, les gens se sont mis à utiliser le « classical module pattern ».

Et la seule façon de créer une nouvelle portée (scope) en JS était de créer une fonction.

L’utilisation du pattern dit « IIFE » (Immediately Invokeed Funtion Expression permet l’encapsulation de variables et de fonctions privées dans le scope d’une fonction anonyme immédiatement invoquée. La valeur de retour de la fonction permet d’exposer les variables et fonctions publiques du « module ».

Cela donne une bonne isolation, mais on introduit quand même un objet dans l’espace de noms global, et ça ne résoud pas le problème de l’ordre de chargement et des dépendances.

Arrive ensuite CommonJS (2009-2014)

CommonJS was a project with the goal to establish conventions on module ecosystem for JavaScript outside of the web browser. The primary reason for its creation was a major lack of commonly accepted form of JavaScript scripts module units which could be reusable in environments different from that provided by a conventional web browser e.g. web server or native desktop applications which run JavaScript scripts.

Avec CommonJS, chaque fichier est un module. Il définit explicitement ses exports, via la variable module.exports, et ses imports via la fonction require().

Les système de modules de Node ressemble beacoup à CommonJS.

Le principal problème, c’est que ce standard n’est pas supporté nativement par les navigateurs, et spécifie un chargement synchrone des modules, ce qui n’est pas toujours idéal.

Arrive alors Asynchronous module definition (AMD)

Une autre API, plus ou moins compatible avec CommonJS (il y a un require()), mais quand même différente (ils privilégient l’utilisation de define()).

Donc des gens ont voulu faire une variante « universelle » (UMD) pour supporter les deux à la fois.

En 2015, après des années de stagnation, le langage a une grosse mise à jour : ES6 alias ES2015

Avec ES6 le langage acquiert un support natif des modules avec les mots-clés import et export.

Côté HTML, il suffit de charger un seul point d’entrée comme ceci :

Mais tout ça n’est supporté que dans les navigateurs modernes :

Donc en pratique, pour utiliser du code JS en modules (CommonJS ou ES2015) dans le navigateur, on peut passer par un outil qui va assembler tous les modules dans un seul gros fichier JS en tenant compte des dépendances : un bundler.

Le plus connu est Webpack, mais il y a aussi Rollup ou Parcel.

Voilà, pour mon topo de newbie sur la question !