Comprendre les modules en javascript

Qu’est ce qu’un module en Javascript?

Un module est un fichier tout simplement rien de plus. On parle de module en javascript moderne, en effet ce concept n’existait pas avant ES6. Les modules existent en NodeJs également. Mais dans cet article je ne vais parler que des modules côté client.

Avant l’existence des modules, on ne pouvait pas importer un fichier Javascript dans un autre fichier Javascript comme on peut le faire en PHP.

Ici on ne va parler que des modules front end, dont la syntaxe est différente des modules dans NodeJS, dans ce dernier il existe au moins deux façon de faire, avec require() (commonJs) et import (AMD).

Comment on faisait avant pour importer un script Javascript?

#index.html
<html>
...
<body>
..
<script src="index.js"></script>
<script src="helper.js"></script>
...

On se servait de la balise script our importer les fichiers, index.js ne pouvait pas importer helper.js. La conséquence est que dans un fichier HTML on pouvait avoir facilement plus d’une dizaine d’import de fichier javascript. Cela pouvait avoir un impoact sur la rapidité du site.

Et vinrent les modules Javascript

Dès qu’il y a un import de modules, on observe au minimum deux fichiers. Soient deux fichiers, un principal.js, et un helper.js

//index.html
<script type="module">
import {sayHi} from './helper.js';

sayHi('John');
</script>
// helper.js
export function sayHi(name){
     console.log('Hello ' +  name)
}

On peut importer une fonction à condition qu’elle ait été exportée depuis le fichier helper.js. Dans la balise script on doit ajouter l’attribut type= »module » pour que cela marche.

Les caractéristiques de modules

Les modules utilisent le mode strict

Dans un code Javascript vous utilisez le mode strict pour forcer une plus grande rigueur dans la programmation

Par exemple dans un fichier HTML

<script>
a = 5  // autorisé pas d'erreur dans la console
</script>

Par contre

<script>
"use strict"
a = 5  // ReferenceError: a is not defined
</script>

de même dans un module
<script type="module">
a = 5 // ReferenceError: a is not defined  bien qu'on n'ait pas eu à mettre use strict 
</script>

Le scope des variables

Chaque module a son scope pour ses variables (je parle des variable globales du module, pas les variable locales des fonctions), les modules ne communiquenet donc pas leur variables, autrement dit, un module ne peut utiliser une variable globales d’un autre module.

// user.js
let user = "Jules"

// main.js
console.log(user) //  

//index.html
<script type="module" src="user.js"></script>
<script type="module" src="hello.js"></script>

la console va afficher "SyntaxError: The requested module './lib.js' does not provide an export named 'user'"

//on modifie dans user.js
export let user = "Jules"  // correct

//Pour illustrer directement dans une page HTML
<script type="module">
    export let user = "Jules"
</script>
<script type="module">
    console.log(user)  // Erreur,et on ne peut faire import du module puisqu'il est inline, la seule façon urait été de faire window.user = "Jules"
</script>

Evaluation une seule fois du module à l’import

//index.html
<script type="module">
    window.test = 0
</script>
<script type="module">
    import { val } from './util.js'

</script>
<script type="module">
    import { val } from './util.js'

    console.log(val)
</script>

// util.js
window.test++
export let val = window.test

bien que l’on ait importé deux fois le module util.js, le code va afficher 1 et non 2

Prenons un autre exemple, qui va montrer que cet aspect est en fait pratique, permet aux différents scripts de se « partager  » une variable

// config.js
export let config = {};

// 1.js
import { config } from './config.js'
config.user = "user1"

//2.js
import { config } from './config.js'
config.url = "http://local.test/user1"

// index.html
<script type="module" src="1.js"></script>
<script type="module" src="2.js"></script>
<script type="module">
    import { config } from './config.js'
    console.log(config)
</script>

L’exemple ci-dessus montre que les fichier 1.js et 2.js apportent des nouvelles propriétés

La propriété meta de import

// en reprenant l'exemple précédent

// index.html
<script type="module" src="1.js"></script>
<script type="module" src="2.js"></script>
<script type="module">
    import { config } from './config.js'
    console.log(import.meta)
</script>

On obtient quelques informations sur le module courant.

Le mot clé this dans un module

Dans un module le mot clé this n’existe pas, il est undefined

<script>
  alert(this); // window
</script>

<script type="module">
  alert(this); // undefined
</script>

Les script module sont deferred dans un client

Ce qui suit n’est valable que dans un navigateur

Vous connaissez l’attribut defer dans la balise script.

<script type="module">
  alert(typeof button); // ce module est defer, il se télécharge immédiatement, mais il attend que le HTML finisse de se télécharger avant de s'exécuter.
</script>


<script>
  alert(typeof button); // s'exécute immédiatement après le téléchargement, avant que le HTML du bouton ne vienne
</script>

<button id="button">Button</button>

Toujours préciser le chemin,

Même si le script est adjacent à votre page HTML, il faut préfixer avec un point slash

import {compteur} from 'util.js'; // dans un navigateur ceci n'est pas autorisé,TypeError: Failed to resolve module specifier "util.js". Relative references must start with either "/", "./", or "../".
import {compteur} from './util.js'; 

scripts externes et CORS

L’inclusion de script externe en tant que module doit se faire avec précaution dans le cas des modules. Pour les scripts externes de même origine que la page HTML, pas de soucis, mais si le script vient d’un autre nom de domaine, alors le serveur d’origine

<script type="module" src="https://site.com/script.js"></script>
il faut que site.com ait activé le CORS.

async et les modules

Le mot clé async permet de télécharger en parallèle un script javascript externe, mais ne s’utilise pas pour les script inline (à même la page HTML). dans le cas des modules, le mot clé async s’applique aussi aux Javascript inline.

async Javascript import
<script async type="module">
  import {counter} from './utils.js';

  counter.count();
</script>

Retour en haut