informatica:programacion:cursos:programacion_avanzada_javascript:modulos_javascript
Diferencias
Muestra las diferencias entre dos versiones de la página.
| Ambos lados, revisión anteriorRevisión previaPróxima revisión | Revisión previa | ||
| informatica:programacion:cursos:programacion_avanzada_javascript:modulos_javascript [2024/10/29 12:04] – [DEMO: Módulos ES6] tempwin | informatica:programacion:cursos:programacion_avanzada_javascript:modulos_javascript [2024/10/30 16:07] (actual) – [Recursos] tempwin | ||
|---|---|---|---|
| Línea 611: | Línea 611: | ||
| Lamentablemente cuando salió ES2015, el cargador de módulos no estaba definido, ni tampoco la API que debía usarse para configurarlo. Esto significaba que **no había en el mercado ningún motor de ECMAScript que soportase los módulos de ES2015 de forma nativa**. Ningún navegador, ni Node.js en ninguna de sus versiones. Afortunadamente **eso ha cambiado y en la actualidad tenemos una especificación completa** del sistema de módulos, y todas las versiones modernas de navegadores // | Lamentablemente cuando salió ES2015, el cargador de módulos no estaba definido, ni tampoco la API que debía usarse para configurarlo. Esto significaba que **no había en el mercado ningún motor de ECMAScript que soportase los módulos de ES2015 de forma nativa**. Ningún navegador, ni Node.js en ninguna de sus versiones. Afortunadamente **eso ha cambiado y en la actualidad tenemos una especificación completa** del sistema de módulos, y todas las versiones modernas de navegadores // | ||
| - | <WRAP center round todo 60%> | + | {{ : |
| - | soporte | + | |
| - | </ | + | |
| La imagen anterior es para el soporte de módulos dinámicos, que son aquellos que se cargan usando el propio lenguaje ECMAScript, a través de la palabra clave '' | La imagen anterior es para el soporte de módulos dinámicos, que son aquellos que se cargan usando el propio lenguaje ECMAScript, a través de la palabra clave '' | ||
| - | <WRAP center round todo 60%> | + | {{ : |
| - | soporte de módulos ES6 con etiqueta | + | |
| - | </ | + | |
| ==== Definiendo un módulo ECMAScript 2015 ==== | ==== Definiendo un módulo ECMAScript 2015 ==== | ||
| Línea 919: | Línea 914: | ||
| ==== Exports nombrados ==== | ==== Exports nombrados ==== | ||
| + | Una de las limitaciones existentes tanto en CommonJS como en AMD es la imposibilidad de exportar más de un valor. En CommonJS se exporta siempre el valor de '' | ||
| + | |||
| + | Lo habitual es que cada módulo exporte un valor por defecto, ya hemos visto como: usando '' | ||
| + | |||
| + | Quizá te puede parecer innecesario que un módulo pueda exportar varios elementos. Pero tiene mucha utilidad si adoptas un punto de vista de desarrollo más funcional. En este paradigma tus módulos pueden ser colecciones de funciones relacionadas, | ||
| + | |||
| + | Vamos a ver un ejemplo de un módulo que exporte dos funciones. Asumamos que el código está en '' | ||
| + | |||
| + | <code javascript> | ||
| + | export function add(...a) { | ||
| + | let accum = 0; | ||
| + | for (let v of a) {accum += v;} | ||
| + | return accum; | ||
| + | } | ||
| + | |||
| + | export function substract(start, | ||
| + | let accum = start; | ||
| + | for (let v of a) {accum -= v;} | ||
| + | return accum; | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | Observa cómo se usa '' | ||
| + | |||
| + | Recuerda: un módulo puede exportar tan solo un valor por defecto y todos los nombrados que desee. | ||
| + | |||
| + | ==== Importación de exports nombrados ==== | ||
| + | |||
| + | ¿Cómo podemos importar un export nombrado? Pues seleccionamos todos aquellos que queremos exportar y aplicamos la desestructuración: | ||
| + | |||
| + | <code javascript> | ||
| + | import {add} from ' | ||
| + | let r1 = add(1,2,3); | ||
| + | let r2 = substract(10, | ||
| + | </ | ||
| + | |||
| + | Observa ahora por un lado que la función '' | ||
| + | |||
| + | Si quisiéramos importar tanto '' | ||
| + | |||
| + | <code javascript> | ||
| + | import {add, substract} from ' | ||
| + | </ | ||
| + | |||
| + | ==== Asignar un nombre local a los exports nombrados ==== | ||
| + | |||
| + | Al importar un '' | ||
| + | |||
| + | <code javascript> | ||
| + | import {add as sum} from ' | ||
| + | let r1 = sum(1,2,3); | ||
| + | let r2 = add(1, | ||
| + | </ | ||
| + | |||
| + | La clave es el '' | ||
| ==== DEMO: Exports nombrados ==== | ==== DEMO: Exports nombrados ==== | ||
| + | Usaremos el entorno que creamos antes para ejecutar módulos ES6. | ||
| + | |||
| + | Fichero '' | ||
| + | |||
| + | <code javascript> | ||
| + | // Función que suma todos los parámetros | ||
| + | export function sum(...a) { | ||
| + | let accum = 0; | ||
| + | | ||
| + | for (let v of a) { | ||
| + | accum += v; | ||
| + | } | ||
| + | | ||
| + | return accum; | ||
| + | } | ||
| + | |||
| + | // Cálculo de la media de los valores | ||
| + | export function avg(...a) { | ||
| + | if (a.length == 0) { | ||
| + | return 0; | ||
| + | } | ||
| + | return sum(...a) / a.length; | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | Ya tenemos listos los módulos para importarlos. Creamos ahora el fichero '' | ||
| + | |||
| + | <code javascript> | ||
| + | import {sum, avg} from ' | ||
| + | |||
| + | console.log(sum(1, | ||
| + | |||
| + | console.log(avg(1, | ||
| + | </ | ||
| + | |||
| + | Creamos el //bundle//: | ||
| + | |||
| + | <code bash> | ||
| + | npm run bundle | ||
| + | </ | ||
| + | |||
| + | Iniciamos un servidor web local (podemos usar [[https:// | ||
| + | |||
| + | <code bash> | ||
| + | ws -p 5000 | ||
| + | </ | ||
| + | |||
| + | Y navegamos a '' | ||
| + | |||
| + | Podríamos cambiar el nombre de lo que importamos de los módulos. Por ejemplo, cambiando el nombre de '' | ||
| + | |||
| + | <code javascript> | ||
| + | import {sum, avg as average} from ' | ||
| + | |||
| + | console.log(sum(1, | ||
| + | |||
| + | console.log(avgerage(1, | ||
| + | </ | ||
| ==== Importar todos los exports ==== | ==== Importar todos los exports ==== | ||
| + | |||
| + | Cuando un módulo de ECMAScript 2015 tiene varios " | ||
| + | |||
| + | <code javascript> | ||
| + | import * as mf from ' | ||
| + | let v1 = mf.add(1, | ||
| + | let v2 = mf.substract(10, | ||
| + | </ | ||
| + | |||
| + | **Cuando se usa el asterisco debe indicarse forzosamente un espacio de nombres**, mediante el uso de '' | ||
| + | |||
| + | ==== Exports nombrados y por defecto ==== | ||
| + | |||
| + | Un módulo ECMAScript **puede tener un export por defecto y tantos exports nombrados como desee**. Cuando sea el caso debemos tener presente que: | ||
| + | |||
| + | * '' | ||
| + | * '' | ||
| + | * '' | ||
| + | |||
| + | Como se puede ver, ¡la sentencia '' | ||
| ==== DEMO: Uso de import con comodín ==== | ==== DEMO: Uso de import con comodín ==== | ||
| + | Tenemos el módulo '' | ||
| + | |||
| + | <code javascript> | ||
| + | export function sum(...a) { | ||
| + | let accum = 0; | ||
| + | |||
| + | for (let v of a) { | ||
| + | accum += v; | ||
| + | } | ||
| + | |||
| + | return accum; | ||
| + | } | ||
| + | |||
| + | export function avg(...a) { | ||
| + | if (a.length == 0) { | ||
| + | return 0; | ||
| + | } | ||
| + | return sum(...a) / a.length; | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | En el fichero '' | ||
| + | |||
| + | <code javascript> | ||
| + | import * as m from ' | ||
| + | |||
| + | console.log(m.sum(1, | ||
| + | |||
| + | console.log(m.avg(1, | ||
| + | </ | ||
| + | |||
| + | Creamos el //bundle//: | ||
| + | |||
| + | <code bash> | ||
| + | npm run bundle | ||
| + | </ | ||
| + | |||
| + | Iniciamos un servidor web local (podemos usar [[https:// | ||
| + | |||
| + | <code bash> | ||
| + | ws -p 5000 | ||
| + | </ | ||
| + | |||
| + | Y navegamos a '' | ||
| ==== ¿Qué podemos exportar? ==== | ==== ¿Qué podemos exportar? ==== | ||
| + | **Un módulo ECMASCript 2015 puede exportar** (ya sea de forma nombrada o por defecto) **cualquier tipo de declaración**. Podemos exportar pues: | ||
| + | |||
| + | * Variables y/o constantes que pueden contener objetos, valores simples o funciones (incluyendo generadores) | ||
| + | * Clases | ||
| + | * Funciones | ||
| + | |||
| + | ==== Exports anónimos ==== | ||
| + | |||
| + | Los exports por defecto pueden ser anónimos. Eso significa que podemos exportar sin necesidad de colocar nombre alguno a lo que estamos exportando. Eso solo podemos hacerlo en los exports por defecto (ya que el módulo que importa siempre indica un nombre local para los exports por defecto). **Los exports nombrados siempre deben tener nombre**. | ||
| + | |||
| + | Por lo tanto el siguiente código es correcto (supongamos que está en el fichero '' | ||
| + | |||
| + | <code javascript> | ||
| + | export function foo() {} | ||
| + | export class Bar {} | ||
| + | </ | ||
| + | |||
| + | Podemos tener el siguiente código: | ||
| + | |||
| + | <code javascript> | ||
| + | function foo() {} // No hay export | ||
| + | class Bar {} // No hay export | ||
| + | // exportamos ambos exports nombrados de golpe | ||
| + | export {foo, Bar}; | ||
| + | </ | ||
| + | |||
| + | En ambos casos el resultado es el mismo (dos exports nombrados ('' | ||
| + | |||
| + | La segunda sintaxis usa un enfoque parecido al //revealing module pattern//: se declara todo lo necesario dentro del módulo y al final se exporta todo lo público. En el caso del // | ||
| + | |||
| + | Acostúmbrate a usar el sistema de módulos de ECMAScript. Recuerda que **hoy ya se soporta de serie en todos los navegadores modernos (e incluso en Node.js)** y en el caso de que debas soportar otros motores puedes usar un transpilador. Así que ¡no hay excusa para no utilizar módulos en tus aplicaciones! aumenta la legibilidad y mantenibilidad de nuestro código al permitir una mayor organización. El sistema de módulos de ES6 es lo suficientemente flexible y potente como para ser usado tanto en pequeños desarrollos como en grandes proyectos. No dudes en experimentar con él y en acostumbrarte a sus posibilidades... ¡pronto descubrirás que no puedes vivir sin él! | ||
| ==== Utilizar módulos desde HTML ==== | ==== Utilizar módulos desde HTML ==== | ||
| + | |||
| + | Hasta ahora has visto como cargar módulos **dinámicamente**, | ||
| + | |||
| + | Si no existiese este soporte, el sistema de módulos se quedaría " | ||
| + | |||
| + | === Cargar un módulo usando < | ||
| + | |||
| + | Para cargar un fichero como si se tratase de un módulo, basta con usar '' | ||
| + | |||
| + | No hay sintaxis definida para " | ||
| + | |||
| + | <WRAP center round important 60%> | ||
| + | Importante: Los módulos cargados vía el tag ''< | ||
| + | </ | ||
| + | |||
| + | Por lo tanto dado el siguiente código: | ||
| + | |||
| + | <code html> | ||
| + | <script type=" | ||
| + | <script src=" | ||
| + | <script defer src=" | ||
| + | </ | ||
| + | |||
| + | El orden de ejecución de los scripts es '' | ||
| + | |||
| + | === El atributo " | ||
| + | |||
| + | De igual modo que ahora disponemos de '' | ||
| + | |||
| + | * La versión con módulos, que cargaríamos via '' | ||
| + | * La versión sin módulos, que cargaríamos con ''< | ||
| ==== DEMO: Cargar módulos desde HTML ==== | ==== DEMO: Cargar módulos desde HTML ==== | ||
| + | |||
| + | Página '' | ||
| + | |||
| + | <code html> | ||
| + | < | ||
| + | < | ||
| + | < | ||
| + | <!-- Como queremos añadir un script JS como módulo, | ||
| + | usaremos el atributo " | ||
| + | --> | ||
| + | <script type=" | ||
| + | </ | ||
| + | < | ||
| + | < | ||
| + | <input type=" | ||
| + | <input type=" | ||
| + | </ | ||
| + | </ | ||
| + | </ | ||
| + | |||
| + | Como vemos, añadimos un módulo inicial, '' | ||
| + | |||
| + | <code javascript> | ||
| + | import {next} from ' | ||
| + | const txt = document.getElementById(' | ||
| + | const btn = document.getElementById(' | ||
| + | btn.addEventListener(' | ||
| + | const value = parseInt(txt.value, | ||
| + | txt.value = next(value); | ||
| + | }, false); | ||
| + | </ | ||
| + | |||
| + | Como vemos, en ese módulo no se exporta nada. Esto pasa con los módulos que se cargan a través de la etiqueta ''< | ||
| + | |||
| + | El módulo '' | ||
| + | |||
| + | <code javascript> | ||
| + | import sum from ' | ||
| + | const next = i => sum(i,1); | ||
| + | export {sum, next}; | ||
| + | </ | ||
| + | |||
| + | El módulo '' | ||
| + | |||
| + | <code javascript> | ||
| + | export default (a,b) => a+b; | ||
| + | </ | ||
| + | |||
| + | |||
| + | <WRAP center round info 60%> | ||
| + | Como hemos visto, ya no es necesario Babel o ningún otro transpilador en las últimas versiones de los navegadores porque soportan los módulos de forma nativa. | ||
| + | </ | ||
| ==== DEMO: Soporte de módulos ES6 en NodeJS ==== | ==== DEMO: Soporte de módulos ES6 en NodeJS ==== | ||
| + | <WRAP center round important 60%> | ||
| + | Este código tiene varios años, es muy probable que ya no funcione igual o que las notas que acompañen esta sección estén desfasadas | ||
| + | </ | ||
| + | Fichero '' | ||
| + | |||
| + | <code javascript> | ||
| + | import {next} from ' | ||
| + | const value = 42; | ||
| + | console.log(next(value)); | ||
| + | </ | ||
| + | |||
| + | <WRAP center round info 60%> | ||
| + | Node.js obliga que los ficheros de módulos tengan la extensión '' | ||
| + | </ | ||
| + | |||
| + | Fichero '' | ||
| + | |||
| + | <code javascript> | ||
| + | import sum from ' | ||
| + | const next = i => sum(i,1); | ||
| + | export {sum, next}; | ||
| + | </ | ||
| + | |||
| + | Fichero '' | ||
| + | |||
| + | <code javascript> | ||
| + | export default (a,b) => a+b; | ||
| + | </ | ||
| + | |||
| + | Ejecutamos con Node.js indicando la característica experimental: | ||
| + | |||
| + | <code bash> | ||
| + | node --experimental-modules index.mjs | ||
| + | </ | ||
| + | ===== Recursos ===== | ||
| + | * [[https:// | ||
| + | * [[https:// | ||
| + | * [[https:// | ||
| + | * [[https:// | ||
| + | * [[https:// | ||
informatica/programacion/cursos/programacion_avanzada_javascript/modulos_javascript.1730199844.txt.gz · Última modificación: por tempwin
