[ Web Proxy ]
URL:
Viewing: https://developer.mozilla.org/es/docs/Web/JavaScript/Guide/Modules [Back]  [Original]

Mdulos JavaScript - JavaScript | MDN

Esta pgina ha sido traducida del ingls por la comunidad. Aprende ms y nete a la comunidad de MDN Web Docs.

View in English Always switch to English

Mdulos JavaScript

Esta gua te brinda todo lo que necesitas para comenzar con la sintaxis de los mdulos JavaScript.

In this article

Un antecedente sobre mdulos

Los programas JavaScript comenzaron siendo bastante pequeos la mayor parte de su uso en los primeros das era para realizar tareas de scripting aisladas, proporcionando un poco de interactividad a tus pginas web donde fuera necesario, por lo que generalmente no se necesitaban grandes scripts. Avancemos unos aos y ahora tenemos aplicaciones completas que se ejecutan en navegadores con mucho JavaScript, JavaScript ahora se usa en otros contextos (Node.js, por ejemplo).

Por lo tanto, en los ltimos aos se ha comenzado a pensar en proporcionar mecanismos para dividir programas JavaScript en mdulos separados que se puedan importar cuando sea necesario. Node.js ha tenido esta capacidad durante mucho tiempo, y hay una serie de bibliotecas y marcos de JavaScript que permiten el uso de mdulos (por ejemplo, CommonJS y AMD otros basados en sistemas de mdulos como RequireJS, y recientemente Webpack y Babel).

La buena noticia es que los navegadores modernos han comenzado a admitir la funcionalidad de los mdulos de forma nativa, y de esto se trata este artculo. Esto solo puede ser algo bueno los navegadores pueden optimizar la carga de mdulos, hacindolo ms eficiente que tener que usar una biblioteca y hacer todo ese procesamiento adicional de lado del cliente, ahorrando viajes de ida y vuelta adicionales.

Compatibilidad con navegadores

Introduccin un ejemplo

Para demostrar el uso de mdulos, hemos creado un sencillo conjunto de ejemplos que puedes encontrar en GitHub. Estos ejemplos demuestran un sencillo conjunto de mdulos que crean un elemento <canvas> en una pgina web, y luego dibujan (y reportan informacin sobre) diferentes formas en el lienzo.

Estos son bastante triviales, pero se han mantenido deliberadamente simples para demostrar los mdulos con claridad.

Nota: Si deseas descargar los ejemplos y ejecutarlos localmente, debers ejecutarlos a travs de un servidor web local.

Estructura bsica de los ejemplos

En nuestro primer ejemplo (ve basic-modules) tenemos la siguiente estructura de archivos:

index.html
main.js
modules/
    canvas.js
    square.js

Nota: Todos los ejemplos de esta gua bsicamente tienen la misma estructura; lo anterior debera empezar a resultarte bastante familiar.

Los dos mdulos del directorio modules se describen a continuacin:

  • canvas.js contiene funciones relacionadas con la configuracin del lienzo (canvas):

    • create() crea un lienzo (canvas) con un width y height especificados dentro de un contenedor <div> con un ID especificado, que a su vez se aade dentro de un elemento padre especificado. Devuelve un objeto que contiene el contexto 2D del lienzo y el ID del contenedor.
    • createReportList() crea una lista desordenada adjunta dentro de un elemento contenedor especfico, que se puede usar para generar datos de informes. Devuelve el ID de la lista.
  • square.js contiene:

    • name una constante que contiene la cadena 'square'.
    • draw() dibuja una figura cuadrada en un lienzo especfico, con un tamao, posicin y color especficos. Devuelve un objeto que contiene el tamao, la posicin y el color del cuadrado.
    • reportArea() escribe el rea de un cuadrado en una lista de informes especfica, dada su longitud.
    • reportPerimeter() escribe el permetro de un cuadrado en una lista de informes especfica, dada su longitud.

Reflexin .mjs versus .js

A travs de este artculo, usaremos extensiones .js para nuestros archivos de mdulo, pero en otros recursos, puedes ver que en su lugar se usa la extensin .mjs. La documentacin de V8 recomienda esto, por ejemplo. Las razones dadas son:

  • Es bueno por claridad, es decir, deja claro qu archivos son mdulos y cules JavaScript.
  • Asegura que tus archivos de mdulo sean analizados como un mdulo por los entornos de ejecucin como Node.js y herramientas de compilacin como Babel.

Sin embargo, decidimos seguir usando .js, al menos por el momento. Para que los mdulos funcionen correctamente en un navegador, debes asegurarte de que tu servidor los est sirviendo con un encabezado Content-Type que contenga un tipo MIME de JavaScript como text/javascript. Si no lo haces, obtendrs un estricto error de verificacin de tipo MIME como "El servidor respondi con un tipo MIME que no es JavaScript" y el navegador no ejecutar tu JavaScript. La mayora de los servidores ya configuran el tipo correcto para archivos .js, pero todava no para archivos .mjs. Los servidores que ya sirven archivos .mjs incluyen GitHub Pages y http-server para Node.js.

Esto est bien si ya ests utilizando un entorno de este tipo, o si no, pero sabes lo que ests haciendo y tiene acceso (es decir, puedes configurar tu servidor para establecer el Content-Type para archivos .mjs). Sin embargo, podra causar confusin si no controlas el servidor desde el que ests sirviendo archivos, o si ests publicando archivos para uso pblico, como lo hacemos aqu.

Por motivos de aprendizaje y portabilidad, decidimos mantenernos en .js.

Si realmente valoras la claridad de usar .mjs para mdulos en lugar de usar .js para archivos JavaScript "normales", pero no quieres encontrarte con el problema descrito anteriormente, siempre puedes usar .mjs durante el desarrollo y convertirlos a .js durante tu paso de compilacin.

Tambin vale la pena sealar que:

  • Es posible que algunas herramientas nunca admitan .mjs, tal como TypeScript.
  • El atributo <script type="module"> se usa para indicar cundo se est apuntando a un mdulo, como vers a continuacin.

Exportar caractersticas del mdulo

Lo primero que debes hacer para acceder a las funciones del mdulo es exportarlas. Esto se hace usando la declaracin export.

La forma ms sencilla de utilizarla es colocarla delante de cualquier elemento que desees exportar fuera del mdulo, por ejemplo:

js
export const name = "square";

export function draw(ctx, length, x, y, color) {
  ctx.fillStyle = color;
  ctx.fillRect(x, y, length, length);

  return {
    length: length,
    x: x,
    y: y,
    color: color,
  };
}

Puedes exportar funciones, var, let, const y, como veremos ms adelante clases. Deben ser elementos de nivel superior; no puedes usar export dentro de una funcin, por ejemplo.

Una forma ms conveniente de exportar todos los elementos que deseas exportar es usar una sola declaracin de exportacin al final de tu archivo de mdulo, seguida de una lista separada por comas de las caractersticas que deseas exportar entre llaves. Por ejemplo:

js
export { name, draw, reportArea, reportPerimeter };

Importacin de caractersticas en tu script

Una vez que hayas declarado las funciones y caractersticas que deseas exportar de tu mdulo, debes importarlas en tu script para poder usarlas. La forma ms sencilla de hacerlo es la siguiente:

js
import { name, draw, reportArea, reportPerimeter } from "./modules/square.js";

Utiliza la declaracin import, seguida de una lista separada por comas de las caractersticas que deseas importar entre llaves, seguida de la palabra clave from, seguida de la ruta al archivo del mdulo una ruta relativa a la raz del sitio, que para nuestro ejemplo de basic-modules sera /js-examples/modules/basic-modules.

Sin embargo, hemos escrito la ruta de manera un poco diferente estamos usando la sintaxis de punto (.) para significar "la ubicacin actual", seguida de la ruta ms all del archivo que estamos tratando de encontrar. Esto es mucho mejor que escribir la ruta relativa completa cada vez, ya que es ms corta y hace que la URL sea porttil el ejemplo seguir funcionando si lo mueve a una ubicacin diferente en la jerarqua del sitio.

As por ejemplo:

/js-examples/modules/basic-modules/modules/square.js

se convierte en

./modules/square.js

Puedes ver estas lneas en accin en main.js.

Nota: En algunos sistemas de mdulos, puedes omitir la extensin del archivo y el punto (por ejemplo, '/modules/square'). Esto no funciona en mdulos de JavaScript nativos.

Una vez que hayas importado las funciones a tu script, las puedes usar tal como se definieron dentro del mismo archivo. Lo siguiente se encuentra en main.js, debajo de las lneas import:

js
let myCanvas = create("myCanvas", document.body, 480, 320);
let reportList = createReportList(myCanvas.id);

let square1 = draw(myCanvas.ctx, 50, 50, 100, "blue");
reportArea(square1.length, reportList);
reportPerimeter(square1.length, reportList);

Nota: Aunque las funciones importadas estn disponibles en el archivo, son vistas de solo lectura de la funcin que se export. No puedes cambiar la variable que se import, pero an puedes modificar propiedades similares a const. Adems, estas caractersticas se importan como enlaces activos, lo cual significa que pueden cambiar de valor incluso si no puedes modificar el enlace a diferencia de const.

Aplicar el mdulo a tu HTML

Ahora solo necesitamos aplicar el mdulo main.js a nuestra pgina HTML. Esto es muy similar a cmo aplicamos un script normal a una pgina, con algunas diferencias notables.

En primer lugar, debes incluir type="module" en el elemento <script>, para declarar este script como un mdulo. Para importar el script main.js, usamos esto:

html
<script type="module" src="main.js"></script>

Tambin puedes incrustar el script del mdulo directamente en el archivo HTML colocando el cdigo JavaScript dentro del cuerpo del elemento <script>:

js
<script type="module">/* El cdigo del mdulo JavaScript va aqu */</script>

El script en el que importas las caractersticas del mdulo bsicamente acta como el mdulo de nivel superior. Si lo omite, Firefox, por ejemplo, te da un error de "SyntaxError: Las declaraciones import solo pueden aparecer en el nivel superior de un mdulo".

Solo puede usar instrucciones import y export dentro de los mdulos, no en scripts normales.

Otras diferencias entre mdulos y scripts estndar

  • Debes prestar atencin a las pruebas locales si intentas cargar el archivo HTML localmente (es decir, con una URL file:///), te encontrars con errores de CORS debido a los requisitos de seguridad del mdulo JavaScript. Necesitas hacer tus pruebas a travs de un servidor.
  • Adems, ten en cuenta que puedes obtener un comportamiento diferente de las secciones del script definidas dentro de los mdulos en comparacin con los scripts estndar. Esto se debe a que los mdulos automticamente usan strict mode.
  • No es necesario utilizar el atributo defer (ve atributos de <script>) al cargar un script de mdulo; los mdulos se difieren automticamente.
  • Los mdulos solo se ejecutan una vez, incluso si se les ha hecho referencia en varias etiquetas <script>.
  • Por ltimo, pero no menos importante, dejemos esto en claro las caractersticas del mdulo se importan al alcance de un solo script no estn disponibles en el alcance global. Por lo tanto, solo podrs acceder a las funciones importadas en el script en el que se importan y no podrs acceder a ellas desde la consola de JavaScript, por ejemplo. Seguirs recibiendo errores de sintaxis en DevTools, pero no podrs utilizar algunas de las tcnicas de depuracin que esperabas utilizar.

Exportaciones predeterminadas vs. exportaciones con nombre

La funcionalidad que hemos exportado hasta ahora se compone de exportaciones con nombre cada elemento (ya sea una funcin, const, etc.) se ha denominado por su nombre en export, y ese nombre tambin se ha utilizado para referirse a l en import.

Tambin hay un tipo de exportacin llamado exportacin predeterminada est diseado para facilitar que un mdulo proporcione una funcin predeterminada, y tambin ayuda a los mdulos JavaScript a interoperar con los sistemas de mdulos CommonJS y AMD existentes (como se explica muy bien en ES6 en profundidad: mdulos de Jason Orendorff; busca "Exportaciones predeterminadas").

Veamos un ejemplo mientras explicamos cmo funciona. En nuestros basic-modules square.js puedes encontrar una funcin llamada randomSquare() que crea un cuadrado con un color, tamao y posicin aleatorios. Lo queremos exportar como nuestro predeterminado, por lo que en la parte inferior del archivo escribimos esto:

js
export default randomSquare;

Ten en cuenta la falta de llaves.

En su lugar, podramos anteponer export default a la funcin y definirla como una funcin annima, as:

js
export default function(ctx) {
  ...
}

En nuestro archivo main.js, importamos la funcin predeterminada usando esta lnea:

js
import randomSquare from "./modules/square.js";

Una vez ms, ten en cuenta la falta de llaves. Esto se debe a que solo se permite una exportacin predeterminada por mdulo, y sabemos que randomSquare lo es. La lnea anterior es bsicamente una abreviatura de:

js
import { default as randomSquare } from "./modules/square.js";

Nota: La sintaxis as para cambiar el nombre de los elementos exportados se explica a continuacin en la seccin Renombrar importaciones y exportaciones.

Evitar conflictos de nombres

Hasta ahora, nuestros mdulos de dibujo de formas en el lienzo parecen estar funcionando bien. Pero, qu pasa si intentamos agregar un mdulo que se ocupa de dibujar otra forma, como un crculo o un tringulo? Estas formas probablemente tambin tendran funciones asociadas como draw(), reportArea(), etc.; si intentramos importar diferentes funciones del mismo nombre en el mismo archivo de mdulo de nivel superior, terminaramos con conflictos y errores.

Afortunadamente, hay varias formas de evitar esto. Los veremos en las siguientes secciones.

Renombrar importaciones y exportaciones

Dentro de las llaves de tu instrucciones import y export, puedes usar la palabra clave as junto con un nuevo nombre de funcin, para cambiar el nombre de identificacin que utilizar una funcin dentro del mdulo de nivel superior.

Entonces, por ejemplo, ambos de los siguientes haran el mismo trabajo, aunque de una manera ligeramente diferente:

js
// dentro de module.js
export { function1 as newFunctionName, function2 as anotherNewFunctionName };

// dentro de main.js
import { newFunctionName, anotherNewFunctionName } from "./modules/module.js";
js
// dentro de module.js
export { function1, function2 };

// dentro de main.js
import {
  function1 as newFunctionName,
  function2 as anotherNewFunctionName,
} from "./modules/module.js";

Veamos un ejemplo real. En nuestro directorio renaming, vers el mismo sistema de mdulos que en el ejemplo anterior, excepto que hemos agregado los mdulos circle.js y triangle.js para dibujar e informar sobre crculos y tringulos.

Dentro de cada uno de estos mdulos, tenemos caractersticas con los mismos nombres que se exportan y, por lo tanto, cada una tiene la misma instruccin export en la parte inferior:

js
export { name, draw, reportArea, reportPerimeter };

Al importarlos a main.js, si intentamos usar esto:

js
import { name, draw, reportArea, reportPerimeter } from "./modules/square.js";
import { name, draw, reportArea, reportPerimeter } from "./modules/circle.js";
import { name, draw, reportArea, reportPerimeter } from "./modules/triangle.js";

El navegador arrojar un error como "SyntaxError: redeclaracin de nombre import" (Firefox).

En su lugar, necesitamos cambiar el nombre de las importaciones para que sean nicas:

js
import {
  name as squareName,
  draw as drawSquare,
  reportArea as reportSquareArea,
  reportPerimeter as reportSquarePerimeter,
} from "./modules/square.js";

import {
  name as circleName,
  draw as drawCircle,
  reportArea as reportCircleArea,
  reportPerimeter as reportCirclePerimeter,
} from "./modules/circle.js";

import {
  name as triangleName,
  draw as drawTriangle,
  reportArea as reportTriangleArea,
  reportPerimeter as reportTrianglePerimeter,
} from "./modules/triangle.js";

Ten en cuenta que podras resolver el problema en los archivos del mdulo, p. ej.

js
// en square.js
export {
  name as squareName,
  draw as drawSquare,
  reportArea as reportSquareArea,
  reportPerimeter as reportSquarePerimeter,
};
js
// en main.js
import {
  squareName,
  drawSquare,
  reportSquareArea,
  reportSquarePerimeter,
} from "./modules/square.js";

Y funcionara igual. El estilo que uses depende de ti, sin embargo, podra decirse que tiene ms sentido dejar el cdigo de tu mdulo tal cual y realizar los cambios en las importaciones. Esto tiene sentido especialmente cuando ests importando desde mdulos de terceros sobre los que no tienes ningn control.

Crear un objeto module

El mtodo anterior funciona bien, pero es un poco complicado y largo. Una solucin an mejor es importar las caractersticas de cada mdulo dentro de un objeto module. La siguiente forma de sintaxis hace eso:

js
import * as Module from "./modules/module.js";

Esto toma todas las exportaciones disponibles dentro de module.js y las hace disponibles como miembros de un objeto Module, dndole efectivamente su propio espacio de nombres. As por ejemplo:

js
Module.function1()
Module.function2()
etc.

De nuevo, veamos un ejemplo real. Si vas a nuestro directorio module-objects, vers el mismo ejemplo nuevamente, pero reescrito para aprovechar esta nueva sintaxis. En los mdulos, las exportaciones estn todas en la siguiente forma simple:

js
export { name, draw, reportArea, reportPerimeter };

Las importaciones, por otro lado, se ven as:

js
import * as Canvas from "./modules/canvas.js";

import * as Square from "./modules/square.js";
import * as Circle from "./modules/circle.js";
import * as Triangle from "./modules/triangle.js";

En cada caso, ahora puedes acceder a las importaciones del mdulo debajo del nombre del objeto especificado, por ejemplo:

js
let square1 = Square.draw(myCanvas.ctx, 50, 50, 100, "blue");
Square.reportArea(square1.length, reportList);
Square.reportPerimeter(square1.length, reportList);

Por lo tanto, ahora puedes escribir el cdigo de la misma manera que antes (siempre que incluyas los nombres de los objetos donde sea necesario), y las importaciones son mucho ms ordenadas.

Mdulos y clases

Como dijimos antes, tambin puedes exportar e importar clases; esta es otra opcin para evitar conflictos en tu cdigo, y especialmente es til si ya tienes el cdigo de tu mdulo escrito en un estilo orientado a objetos.

Puedes ver un ejemplo de nuestro mdulo de dibujo de formas reescrito con clases ES en nuestro directorio classes. Como ejemplo, el archivo square.js ahora contiene toda su funcionalidad en una sola clase:

js
class Square {
  constructor(ctx, listId, length, x, y, color) {
    ...
  }

  draw() {
    ...
  }

  ...
}

que luego exportamos:

js
export { Square };

En main.js, lo importamos as:

js
import { Square } from "./modules/square.js";

Y luego usas la clase para dibujar nuestro cuadrado:

js
let square1 = new Square(myCanvas.ctx, myCanvas.listId, 50, 50, 100, "blue");
square1.draw();
square1.reportArea();
square1.reportPerimeter();

Carga esttica de mdulos

Habr ocasiones en las que querrs agregar mdulos juntos. Es posible que tengas varios niveles de dependencias, donde desees simplificar las cosas, combinando varios submdulos en un mdulo principal. Esto es posible utilizando la sintaxis de exportacin de los siguientes formas en el mdulo principal:

js
export * from "x.js";
export { name } from "x.js";

Para ver un ejemplo, ve nuestro directorio module-aggregation. En este ejemplo (basado en nuestro ejemplo de clases anterior) tenemos un mdulo adicional llamado shapes.js, que rene toda la funcionalidad de circle.js, square.js y triangle.js. Tambin hemos movido nuestros submdulos dentro de un subdirectorio dentro del directorio modules llamado shapes. Entonces, la estructura del mdulo en este ejemplo es:

modules/
  canvas.js
  shapes.js
  shapes/
    circle.js
    square.js
    triangle.js

En cada uno de los submdulos, la exportacin es de la misma forma, p. ej.

js
export { Square };

Luego viene la parte de agregacin. Dentro de shapes.js, incluimos las siguientes lneas:

js
export { Square } from "./shapes/square.js";
export { Triangle } from "./shapes/triangle.js";
export { Circle } from "./shapes/circle.js";

Estas toman las exportaciones de los submdulos individuales y las ponen a disposicin de manera efectiva desde el mdulo shapes.js.

Nota: Las exportaciones a las que se hace referencia en shapes.js bsicamente se redirigen a travs del archivo y realmente no existen all, por lo que no podrs escribir ningn cdigo relacionado til dentro del mismo archivo.

Entonces, ahora en el archivo main.js, podemos obtener acceso a las tres clases de mdulos reemplazando

js
import { Square } from "./modules/square.js";
import { Circle } from "./modules/circle.js";
import { Triangle } from "./modules/triangle.js";

con la siguiente nica lnea:

js
import { Square, Circle, Triangle } from "./modules/shapes.js";

Carga dinmica de mdulos

La parte ms nueva de la funcionalidad de los mdulos de JavaScript que estar disponible en los navegadores es la carga dinmica de mdulos. Esto te permite cargar mdulos dinmicamente solo cuando son necesarios, en lugar de tener que cargar todo por adelantado. Esto tiene algunas obvias ventajas de rendimiento; sigue leyendo y veamos cmo funciona.

Esta nueva funcionalidad te permite llamar a import() como una funcin, pasndole la ruta al mdulo como parmetro. Devuelve una Promise, que se cumple con un objeto module (consulta Crear un objeto module) que te da acceso a las exportaciones de ese objeto, p. ej.

js
import("./modules/myModule.js").then((module) => {
  // Haz algo con el mdulo.
});

Veamos un ejemplo. En el directorio dynamic-module-import tenemos otro ejemplo basado en nuestro ejemplo de clases. Esta vez, sin embargo, no dibujamos nada en el lienzo cuando se carga el ejemplo. En su lugar, incluimos tres botones "Crculo", "Cuadrado" y "Tringulo" que, cuando se presionan, cargan dinmicamente el mdulo requerido y luego lo usan para dibujar la forma asociada.

En este ejemplo, solo hemos realizado cambios en nuestros archivos index.html y main.js el mdulo exports sigue siendo el mismo que antes.

En main.js hemos tomado una referencia a cada botn usando una llamada a Document.querySelector(), por ejemplo:

js
let squareBtn = document.querySelector(".square");

Luego adjuntamos un escucha de eventos a cada botn para que cuando se presione, el mdulo relevante se cargue dinmicamente y se use para dibujar la forma:

js
squareBtn.addEventListener("click", () => {
  import("./modules/square.js").then((Module) => {
    let square1 = new Module.Square(
      myCanvas.ctx,
      myCanvas.listId,
      50,
      50,
      100,
      "blue",
    );
    square1.draw();
    square1.reportArea();
    square1.reportPerimeter();
  });
});

Ten en cuenta que, debido a que el cumplimiento de la promesa devuelve un objeto module, la clase se convierte en una subfuncin del objeto, por lo que ahora necesitamos acceder al constructor prefijado con Module., p. ej. Module.Square(...).

Solucin de problemas

Aqu hay algunos consejos que te pueden ayudar si tienes problemas para hacer que tus mdulos funcionen. No dude en agregarlos a la lista si descubres ms!

  • Mencionamos esto antes, pero para reiterar: los archivos .js se deben cargar con un tipo MIME de text/javascript (u otro tipo MIME compatible con JavaScript, pero se recomienda text/javascript), de lo contrario, obtendrs un error de comprobacin de tipo MIME estricto como "El servidor respondi con un tipo MIME no JavaScript".
  • Si intentas cargar el archivo HTML localmente (es decir, con una URL file:///), encontrars errores CORS debido a los requisitos de seguridad del mdulo JavaScript. Necesitas hacer tus pruebas a travs de un servidor. Las pginas de GitHub son ideales ya que tambin sirven archivos .js con el tipo MIME correcto.
  • Debido a que .mjs es una extensin de archivo no estndar, es posible que algunos sistemas operativos no la reconozcan o intenten reemplazarla por otra. Por ejemplo, descubrimos que macOS silenciosamente agregaba .js al final de los archivos .mjs y luego, automticamente ocultaba la extensin del archivo. Entonces, todos nuestros archivos salan realmente como x.mjs.js. Una vez que desactivamos la accin de ocultar automticamente las extensiones de archivo y lo entrenamos para aceptar .mjs, todo estuvo bien.

Ve tambin


Web Proxy Viewer  |  New URL  |  Original Page