11 février 2020

Importations dynamiques

Les déclarations d’exportation et d’importation décrites dans les chapitres précédents sont appelées “statiques”. La syntaxe est très simple et stricte.

Premièrement, nous ne pouvons générer dynamiquement aucun paramètre d’import.

Le chemin du module doit être une chaîne de caractères, il ne peut pas être un appel de fonction. Exemple, cela ne fonctionnera pas:

import ... from getModuleName(); // Erreur, seulement une chaîne de caractères est autorisé

Deuxièmement, nous ne pouvons pas importer de manière conditionnelle ou au moment de l’exécution:

if(...) {
  import ...; // Erreur, pas autorisé!
}

{
  import ...; // Erreur, nous ne pouvons pas importer dans un bloc
}

C’est parce que import/export vise à fournir une structure de base à la structure du code. C’est une bonne chose, car la structure du code peut être analysée, les modules peuvent être rassemblés et regroupés dans un fichier à l’aide d’outils spéciaux, les exportations inutilisées peuvent être supprimées (“tree-shaken”). Cela n’est possible que parce que la structure des importations / exportations est simple et fixe.

Mais comment importer un module de manière dynamique, à la demande?

L’expression import()

L’expression import(module) charge le module et renvoie une promesse résolue en un objet de module contenant toutes ses exportations. Il peut être appelé de n’importe quel endroit du code.

Nous pouvons l’utiliser dynamiquement à n’importe quel endroit du code, par exemple:

let modulePath = prompt("Which module to load?");

import(modulePath)
  .then(obj => <module object>)
  .catch(err => <loading error, e.g. si ce module n'existe pas>)

Ou bien, nous pourrions utiliser let module = await import(modulePath) s’il se trouve dans une fonction asynchrone.

Par exemple, si nous avons le module suivant, say.js:

// 📁 say.js
export function hi() {
  alert(`Hello`);
}

export function bye() {
  alert(`Bye`);
}

…Alors l’importation dynamique peut être comme ça:

let {hi, bye} = await import('./say.js');

hi();
bye();

Ou, si say.js a l’exportation par défaut:

// 📁 say.js
export default function() {
  alert("Module loaded (export default)!");
}

…Ensuite, pour y accéder, nous pouvons utiliser la propriété default de l’objet module:

let obj = await import('./say.js');
let say = obj.default;
// ou en une ligne: let {default: say} = await import('./say.js');

say();

Voici l’exemple complet:

Résultat
say.js
index.html
export function hi() {
  alert(`Hello`);
}

export function bye() {
  alert(`Bye`);
}

export default function() {
  alert("Module loaded (export default)!");
}
<!doctype html>
<script>
  async function load() {
    let say = await import('./say.js');
    say.hi(); // Hello!
    say.bye(); // Bye!
    say.default(); // Module loaded (export default)!
  }
</script>
<button onclick="load()">Click me</button>
Veuillez noter :

Les importations dynamiques fonctionnent dans des scripts standard, elles n’exigent pas de script type="module".

Veuillez noter :

Bien que import() ressemble à un appel de fonction, il s’agit d’une syntaxe spéciale qui utilise des parenthèses (similaire à super()).

Nous ne pouvons donc pas copier import dans une variable ni utiliser call/apply avec elle. Ce n’est pas une fonction.

Carte du tutoriel

Commentaires

lire ceci avant de commenter…
  • Si vous avez des améliorations à suggérer, merci de soumettre une issue GitHub ou une pull request au lieu de commenter.
  • Si vous ne comprenez pas quelque chose dans l'article, merci de préciser.
  • Pour insérer quelques bouts de code, utilisez la balise <code>, pour plusieurs lignes -- enveloppez-les avec la balise <pre>, pour plus de 10 lignes - utilisez une sandbox (plnkr, jsbin, codepen…)