Les projets web deviennent de plus en plus complexes, avec de plus en plus de dépendances. Les imports de librairies JavaScript se font par défaut de façon statique en utilisant des balises script intégrées dans le code de la page HTML. Cela peut poser des problèmes dans des applications web monopage, où tout le code de l’application est embarqué dans une seule page HTML avec son code embarqué dans des fichiers JavaScript qui doivent être chargés par le navigateur au chargement de la page. L’import Dynamic permet, lui, d’utiliser un code lorsque ces dépendances sont utilisées uniquement. Pour des dépendances qui ne sont que rarement utilisées, cela fait du code en plus à être récupéré, parsé et exécuté par le navigateur, ce que rallonge le temps de chargement de la page.

Une première stratégie pour éviter ça est le code splitting. Concrètement, on crée plusieurs fichiers JavaScript avec des sous ensembles du code qui sont nécessaires au fonctionnement de chaque page. Chaque page ne contiendra que le code nécessaire à son exécution. En plus de ça, il y a aussi des stratégies d’élimination du code mort, qui se basent sur une analyse statique du code pour éviter l’inclusion de code qui n’est jamais appelé par le code de la page dans le package JavaScript à télécharger.

Par contre ces stratégies ne sont pas suffisantes dans tous les contextes, par exemple, dans une application web monopage, on sera obligé de charger tous les modules de l’application, même s’ils ne sont que très rarement utilisés. Par exemple, si on écrit un client mail, et on fournit un correcteur orthographique, idéalement, on préférerait charger ce module que lors que l’utilisateur commence à éditer son premier message, pas avant. Un utilisateur qui n’édite pas de messages, bénéficierait donc d’un temps de chargement plus court, car, un module en moins à télécharger / charger.

Dans cet article, on parlera du support à l’import dynamic de modules JavaScript qui est en cours de standardisation et qui doit être officiellement partie du langage dans les deux prochaines années. Même si ce n’est pas encore dans le standard, l’import dynamique de code JavaScript est déjà supporté par des transpilers tels que Webpack et Typescript.

Comment utiliser le support à l’import dynamic ?

Un import dynamique en JavaScript se fait avec la fonction import, qui prends en entrée le nom du module à importer sous forme de chaîne de characters et renvoie le module chargé sous forme d’une Promise. Le chargement du module est fait donc de façon asynchrone.

Illustrons le concept dans un exemple pratique.

Commençons avec notre module correcteur orthographique suivant, qu’on mettra dans un fichier spell.js. Il contient une fonction check(word) qui vérifie si un mot est présent dans le dictionnaire ou pas.

const dictionary = [ 'the', 'book', 'is', 'on', 'table' ];

export function check(word) {
    return dictionary.indexOf(word) != -1;
}

console.log('Spell checker module loaded');

Pour utiliser un module, on le référence en utilisant le mot clé import, comme dans l’exemple suivant, où on écrit une fonction toUnderline(message) qui retourne une liste de mots d’un message qui ne sont pas présents dans le dictionnaire.

import * as spell from './spell.js';

function toUnderline(message) {
    return message.split(' ').filter(word => !spell.check(word));
}

let errors = toUnderline('the bok is on the table');
console.log(errors);

Le module spell sera donc chargé systématiquement, peu importe si la fonction toUnderline est appelée ou pas. Faites le test en chargeant ce module avec ou sans l’appel de la fonction toUnderline.

Si on utilise l’import dynamique de modules, on réécrira l’exemple comme ça:

async function toUnderline(message) {
    let spell = await import('./spell.js');

    return message.split(' ').filter(word => !spell.check(word)); 
}

toUnderline('the bok is on the table')
    .then(errors => console.log(errors));

Dans cette version, le module spell ne sera chargé que si la fonction toUnderline est appelée. L’appel à import est fait de façon asynchrone et retour une Promise. On utilise donc le mot clé await pour récupérer le module une fois chargé et on mets le tout dans une fonction async.

Faites le test en chargeant ce module avec ou sans l’appel de la fonction toUnderline. Vous verrez que le module n’est chargé que se la fonction toUnderline est appelée.

Pour aller plus loin