Introduccion a los modulos en JavaScript
A medida que nuestras aplicaciones crecen, queremos dividirlas en múltiples archivos, conocidos como “módulos”. Un módulo puede contener una clase o una biblioteca de funciones destinadas a un propósito específico.
Durante mucho tiempo, JavaScript no tuvo una sintaxis de módulos a nivel de lenguaje. Esto no era un problema inicialmente, ya que los scripts eran pequeños y simples.
Sin embargo, a medida que los scripts se volvieron más complejos, la comunidad desarrolló diversas formas de organizar el código en módulos, utilizando bibliotecas especiales para cargar módulos según sea necesario.
Algunos de estos métodos históricos incluyen:
- AMD (Asynchronous Module Definition): uno de los primeros sistemas de módulos, implementado inicialmente por la biblioteca require.js.
- CommonJS: un sistema de módulos creado para el entorno de servidor Node.js.
- UMD (Universal Module Definition): un sistema de módulos diseñado para ser compatible tanto con AMD como con CommonJS.
Aunque estos sistemas se utilizan menos hoy en día, todavía se pueden encontrar en scripts antiguos.
En 2015, se introdujo en el estándar un sistema de módulos a nivel de lenguaje, que ha evolucionado gradualmente y ahora es compatible con todos los navegadores principales y Node.js. En adelante, estudiaremos los módulos modernos de JavaScript.
¿Qué es un módulo?
Un módulo es simplemente un archivo. Un script es un módulo. Tan sencillo como eso.
Los módulos pueden cargarse entre sí y usar las directivas especiales export
e import
para compartir funcionalidades, permitiendo llamar a funciones de un módulo desde otro.
- La palabra clave
export
marca las variables y funciones que deben ser accesibles desde fuera del módulo actual. import
permite importar funcionalidades de otros módulos.
Por ejemplo, si tenemos un archivo sayHi.js
que exporta una función:
// 📁 sayHi.js
export function sayHi(user) {
alert(`Hello, ${user}!`);
}
Otro archivo puede importarlo y usarlo:
// 📁 main.js
import {sayHi} from './sayHi.js';
alert(sayHi); // function...
sayHi('John'); // Hello, John!
La directiva import
carga el módulo desde la ruta ./sayHi.js
, relativa al archivo actual, y asigna la función exportada sayHi
a la variable correspondiente.
Para que el navegador trate un script como un módulo, debemos usar el atributo <script type="module">
.
Por ejemplo:
El navegador buscará y evaluará automáticamente el módulo importado (y sus importaciones si es necesario), y luego ejecutará el script.
Características principales de los módulos
Los módulos difieren de los scripts “normales” en varios aspectos:
Modo estricto por defecto: Los módulos siempre operan en modo estricto. Por ejemplo, asignar una variable sin declararla resultará en un error.
Ámbito a nivel de módulo: Cada módulo tiene su propio ámbito de nivel superior. Las variables y funciones de nivel superior de un módulo no son visibles para otros scripts.
// user.js
export let user = "John";
// hello.js
import {user} from './user.js';
document.body.innerHTML = user; // John
Evaluación única: El código de un módulo se evalúa solo la primera vez que se importa. Las exportaciones se comparten entre todos los importadores subsecuentes.
// admin.js
export let admin = {
name: "John"
};
// 1.js
import {admin} from './admin.js';
admin.name = "Pete";
// 2.js
import {admin} from './admin.js';
alert(admin.name); // Pete
import.meta: El objeto import.meta
contiene información sobre el módulo actual, como la URL del script en el navegador.
this en módulos: En un módulo, this
es undefined
en el nivel superior.
Funciones específicas del navegador
Los módulos tienen algunas características específicas en el navegador:
- Los módulos son diferidos: Los módulos se cargan en paralelo junto con otros recursos y se ejecutan una vez que el documento HTML está completamente cargado.
Scripts externos duplicados: Los scripts externos con el mismo src
solo se ejecutan una vez.
Compatibilidad con navegadores antiguos: Los navegadores antiguos no entienden type="module"
. Podemos proporcionar una alternativa usando nomodule
.
Herramientas de empaquetado
En la práctica, los módulos de navegador rara vez se utilizan en su forma pura. Generalmente, se agrupan con herramientas como Webpack para mejor rendimiento y otras razones.
Estas herramientas:
- Toman un módulo principal y analizan sus dependencias.
- Compilan un archivo único con todos los módulos.
- Aplican transformaciones y optimizaciones, como la eliminación de código no utilizado.
- Minimizan el archivo resultante.
Por ejemplo, el script empaquetado resultante no contiene import/export
y se puede colocar en un script normal:
Resumen
Para resumir, los conceptos clave son:
- Un módulo es un archivo. Para que
import/export
funcione, los navegadores necesitan<script type="module">
. - Los módulos tienen varias diferencias con los scripts normales, como el modo estricto por defecto y un ámbito de nivel superior independiente.
- El código del módulo se ejecuta solo una vez y las exportaciones se comparten entre importadores.
- En la producción, se suelen usar herramientas de empaquetado como Webpack para agrupar módulos y optimizar el rendimiento.
En el próximo capítulo, veremos más ejemplos de módulos y cómo exportar e importar diversas funcionalidades.