Importaciones dinámicas en JavaScript
Las instrucciones de exportación e importación que discutimos en capítulos anteriores se denominan «estáticas». La sintaxis es bastante sencilla y estricta.
En primer lugar, no podemos generar dinámicamente ningún parámetro de import
.
La ruta del módulo debe ser una cadena de texto literal, no puede ser una llamada a una función. Esto no funcionará:
import ... from getModuleName(); // Error, from solo permite "string"
En segundo lugar, no podemos importar de manera condicional o en tiempo de ejecución:
if(...) {
import ...; // ¡Error, no permitido!
}
{
import ...; // Error, no podemos poner importación en ningún bloque.
}
Esto se debe a que las instrucciones import/export
proporcionan una base sólida para la estructura del código. Esto es beneficioso porque la estructura del código puede ser analizada, los módulos pueden ser ensamblados y agrupados en un archivo mediante herramientas especializadas, y las exportaciones no utilizadas pueden ser eliminadas («tree-shaken»). Esto es posible solo porque la estructura de las importaciones/exportaciones es simple y fija.
Pero, ¿cómo podemos importar un módulo de forma dinámica, según sea necesario?
La expresión import()
La expresión import(module)
carga el módulo y devuelve una promesa que se resuelve en un objeto de módulo que contiene todas sus exportaciones. Se puede llamar desde cualquier lugar del código.
Podemos usarlo de manera dinámica en cualquier parte del código, por ejemplo:
let modulePath = prompt("¿Qué módulo cargar?");
import(modulePath)
.then(obj => )
.catch(err => )
O, podríamos usar let module = await import(modulePath)
si está dentro de una función asíncrona.
Por ejemplo, si tenemos el siguiente módulo say.js
:
// 📁 say.js
export function hi() {
alert(`Hola`);
}
export function bye() {
alert(`Adiós`);
}
…Entonces la importación dinámica podría ser así:
let {hi, bye} = await import('./say.js');
hi();
bye();
O, si say.js
tiene una exportación predeterminada:
// 📁 say.js
export default function() {
alert("¡Módulo cargado (exportación predeterminada)!");
}
…Luego, para acceder a él, podemos usar la propiedad default
del objeto del módulo:
let obj = await import('./say.js');
let say = obj.default;
// o, en una línea: let {default: say} = await import('./say.js');
say();
Aquí está el ejemplo completo:
Notas importantes
Las importaciones dinámicas funcionan en scripts normales, no requieren
script type="module"
.Aunque
import()
parece una llamada a una función, es una sintaxis especial que solo usa paréntesis (similar asuper()
).
Por lo tanto, no podemos copiar import
a una variable o usar call/apply
con ella. No es una función.