Skip to content

Latest commit

 

History

History
752 lines (608 loc) · 24.1 KB

File metadata and controls

752 lines (608 loc) · 24.1 KB

shieldsIO shieldsIO shieldsIO

WideImg

Máster en Programación FullStack con JavaScript y Node.js

JS, Node.js, Frontend, Backend, Firebase, Express, Patrones, HTML5_APIs, Asincronía, Websockets, Testing

Clase 58

IMG

JavaScript Modular

En programación, un módulo es una porción de un programa de ordenador. De las varias tareas que debe realizar un programa para cumplir con su función u objetivos, un módulo realizará, comúnmente, una de dichas tareas (o varias, en algún caso).

En general (no necesariamente relacionado con la programación), un módulo recibe como entrada la salida que haya proporcionado otro módulo o los datos de entrada al sistema (programa) si se trata del módulo principal de éste; y proporcionará una salida que, a su vez, podrá ser utilizada como entrada de otro módulo o bien contribuirá directamente a la salida final del sistema (programa), si se retorna al módulo principal.

Particularmente, en el caso de la programación, los módulos suelen estar (no necesariamente) organizados jerárquicamente en niveles, de forma que hay un módulo principal que realiza las llamadas oportunas a los módulos de nivel inferior.

Cuando un módulo es convocado, recibe como entrada los datos proporcionados por otro del mismo o superior nivel, el que ha hecho la llamada; luego realiza su tarea. A su vez este módulo convocado puede llamar a otro u otros módulos de nivel inferior si fuera necesario; cuando ellos finalizan sus tareas, devuelven la salida pertinente al módulo inmediato llamador, en secuencia reversa. Finalmente se continúa con la ejecución del módulo principal. Wikipedia

Las claves

  • Los modulos son "pequeños" fragmentos de codigo muy especializados
  • Los modulos son "independientes" y remplazables por otros
  • Los modulos tienen dependencias entre si, en ocasiones cruzadas
  • Los modulos premiten encapsular mucha lógica en forma de cajas negras
  • Una buena modularizacón permite subir el nivel de abstraccion de una aplicación
  • Existen muchas formas de gestionar la carga y dependencia de modulos en JavaScript

Las ventajas

  • Desacoplamineto, que nos ayudará con las refactorizaciones futuras
  • Extensibildiad del código
  • Alta reutilización del código
  • División clara del código, lo que facilita la escalabildiad y el mantenimiento
  • Es más sencillo que otros sistemas de gestión de dependencias
  • Facilemnte automatizable, después de aprender un nuevo workflow

Las desventajas

  • Necesitamos dependencias nuevas para gestioanr esto
  • Necesitamos pensar de forma modular, lo que implica más código y artilugios

Dependencias y modularidad Modularidad

Recursos

JavaScript Modular: Sin esquemas, ni planes...

IMG

¿Te suena esto...?

<script src="myjavascript1.js"></script>
<script src="myjavascript2.js"></script>
<!-- ... -->
<script src="myjavascript10.js"></script>
index.html
static/
   js/
     app/
     vendor/
   css/
     app/
     vendor/

Problemas

  • Interdependecias horibles
  • Mucho, mucho, mucho... ruido en cada fichero
  • Demasiado código para leer y entender que pasa
  • Demasiada interconexión, imposible refacorizar
  • Un cambio mediano puede significar volver a empezar el proyecto de cero
  • No es posible reutilizar nada
  • :trollface: Nuestros datos y lógica de negocio se extienden globlamente por todas partes
  • Lentitud de carga en toda la web, demasiadas cosas sincronas tienen que cargar

JavaScript Modular: Patrones

IMG

Usemos lo que conocemos

  • Namespace Pattern
  • Revealing Module Pattern
  • Module Pattern
  • Singleton Pattern

Limitaciones para la implementación directa

  • Poca abstracción en la aplicación
  • Mucho código para gestionar cada módulo
  • Gestión muy manual y rigida
  • La carga asincrona es un reto técnico
  • Las dependencias circulares son un tema delicado de gestionar

JavaScript Modular: CommonJS

IMG

¿Cómo funciona?

  • Fichero math.js
//@see: http://www.commonjs.org/specs/modules/1.0/
exports.add = function() {
    var sum = 0, i = 0, args = arguments, l = args.length;
    while (i < l) {
        sum += args[i++];
    }
    return sum;
};

//ES6 Version
exports.add = function(...numbers) {
    return numbers.reduce((previous, current) => {
      return previous + current;
    });
};
  • Fichero increment.js
//@see: http://www.commonjs.org/specs/modules/1.0/
var add = require('math').add;
exports.increment = function(val) {
    return add(val, 1);
};
  • Fichero program.js
//@see: http://www.commonjs.org/specs/modules/1.0/
var inc = require('increment').increment;
var a = 1;
inc(a); // 2

Las claves

  • Más extendido en el backend, especialmente Node
  • Simple y sencillo, no requiere una curva de aprendizaje
  • Soporta dependencias circulares
  • Una espeficicación sencilla

Los problemas

  • Un archivo por modulo
  • La carga por definición es sincrona, pero existen workarounds (Ajax)
  • No se puede implementar sin una librería

Librerias Específicas

Recursos

JavaScript Modular: Asynchronous Module Definition (AMD)

IMG

¿Cómo funciona?

  • Fichero math.js
define("add", function () {
    var sum = 0, i = 0, args = arguments, l = args.length;
    while (i < l) {
        sum += args[i++];
    }
    return sum;
});

//ES6 Version
define("add", (...numbers) => {
    return numbers.reduce((previous, current) => {
      return previous + current;
    });
};
  • Fichero increment.js
define("increment", ["add"], function (add) {
    return add(val, 1);
});
  • Fichero program.js
require(['increment'], function ( inc ) {
  var a = 1;
  inc(a); // 2
});

Las claves

  • Historicamente más extendido en el frontend, Angula 1.x, Backbone, etc...
  • Creado por developers decontentos con Common.js y su carga síncrona
  • Soporta Plugins
  • Los modulos pueden romperse en multiples ficheros
  • Asincronia de base
  • Compatibildiad con requirey exports
  • Buen soporte a dependencias circulares

Los problemas

  • Demasiado verboso, mucha decoración...
  • No funciona sin librerias externas

Librerías

Recursos

JavaScript Modular: ECMAScript 6 modules (Native JavaScript)

⚠️ IMPORTANTE: Soporte limitado aún :-(

¿Cómo sería?

  • Fichero math.js
export function add(...numbers) {
  return numbers.reduce((previous, current) => {
    return previous + current;
  });
}
  • Fichero increment.js
import {add} from 'math';

export function increment(val) {
    return add(val, 1);
}
  • Fichero program.js
import {increment as inc} from 'increment';

const a = 1;
inc(a); // 2

🎁 Bonus: Importación asíncrona

  • Asíncrona (solo un módulo)
System.import('modulo')

  .then(modulo => {
      // Uso del módulo importado
  })
  .catch(error => {
      // Gestión de errores
  });
  • Asíncrona (multiples módulos)
Promise.all(
    ['module1', 'module2', 'module3']
    .map(x => System.import(x)))
.then(([module1, module2, module3]) => {
    // Use module1, module2, module3
});

Las claves

  • Sencillo, sencillo, sencillo y mucho más simple
  • asincrono y sincrono de igual forma
  • Ya viene de fabrica con ES6
  • Soporta dependencias circulares

Los problemas

  • Soporte :-(
  • Tenemos que usar babel para transpilarlo

JavaScript Modular: Bundles

IMG

El problema

  • Tenemos demasiados ficheros con muchas dependencias cruzadas
  • A veces la carga es muy lenta y compleja
  • Tenemos que descargar muchos ficheros distintos en tiempo de ejecucion
  • Tenemos limites en cuanto al volumen de ficheros que podemos descargar
  • HTTP1.1 tiene muchas limitaciones que rompera http2.0, pero aún no es standard

La idea

  • Fusionamos los ficheros del mismo tipo en uno solo
  • Servimos un único fichero por tipo
  • Hacemos una versión smart, quitando todo lo que nos sobra o no se ejecutará
  • Minificamos y comprimimos la información todo lo posible

Bower ha muerto

img

BOWER is dead. Only use it for legacy reasons.

Literatura al respecto

La nueva generación

Paso 1: Tus herramientas son las de siempre

Paso 2: Pasar del bower.json al package.json

Paso 3: Deja de usar wiredep y empieza a usar solamente require()

Nota: Esto es al estilo Browserify exclusivamente

Extra 1: Cosas Interesantes

Extra 2: Un mundo de información te espera

Browserify

Logo

Caracteristicas

  • solo soporta el patrón CommonJS, usaremos require() en el browser
  • Genera un fichero bundle con todo el js
  • Se puede usar con un fichero de configuración complejo
  • Se instala globalmente npm install browserify -g

Recursos

Ejemplo

Nuestro fichero public/main.js:

var foo = require('./foo.js');
var bar = require('../lib/bar.js');
var gamma = require('gamma');

var elem = document.getElementById('result');
var x = foo(100) + bar('baz');
elem.textContent = gamma(x);

Nuestro fichero public/foo.js:

module.exports = function (n) { return n * 111 }

Comando que hace toda la magia:

browserify main.js > bundle.js

Nuestro fichero public/index.html

<!doctype html>
<html>
  <head>
    <title>Browserify Playground</title>
  </head>
  <body>
    <script src="/static/js/bundle.js"></script>
  </body>
</html>

Require.js & AMD

img

Documentación

Recursos

Require.js & AMD: En la práctica...

Código convencional (Código espagueti)

index.html

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Testing Requirejs</title>
</head>
<body>
  <script src="calculadora.js"></script>
</body>
</html>

calculadora.js

function sumar (x, y) {
    return x+y
};

function restar (x, y) {
    return x-y
};

function multiplicar (x, y) {
    return x*y
};


function dividir (x, y) {
    return x/y
};

Solución sencilla: Creando un objeto

calculadora.js

var calculadora = {};

calculadora.sumar = function (x,y) {
    return x + y
};
calculadora.restar = function (x, y) {
    return x - y
}
calculadora.multiplicar = function (x, y) {
    return x * y
}
calculadora.divir = function (x, y) {
    return x / y
}

Solución modular: Namespace Pattern index.html

<script src="sumar.js"></script>
<script src="restar.js"></script>
<script src="divir.js"></script>
<script src="multiplicar.js"></script>

sumar.js

var calculadora = calculadora || {};
calculadora.sumar = function (x,y) {
    return x + y
};

restar.js

var calculadora = calculadora || {};
calculadora.restar = function (x, y) {
    return x - y
}

dividir.js

var calculadora = calculadora || {};
calculadora.divir = function (x, y) {
    return x / y
}

multiplicar.js

var calculadora = calculadora || {};
calculadora.multiplicar = function (x, y) {
    return x * y
}

Usando Require.js

index.html

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Testing Requirejs</title>
</head>
<body>
  <script data-main="calculadora" src="require.js"></script>
</body>
</html>

calculadora.js

require(['calculadora/sumar', 'calculadora/restar', 'calculadora/cuadrado'], function (sum, res, cua) {
  console.info(sum(1,2)); // 1 + 2
  console.info(res(3,1)); // 3 - 1
  console.log(cua(2)); // 2 * 2
});

calculadora/sumar.js

define(function () {
  return function (x, y) {
      return x + y;
  };
});

calculadora/restar.js

define(function () {
  return function (x, y) {
      return x - y;
  };
});

calculadora/multiplicar.js

define(function () {
  return function (x, y) {
      return x * y;
  };
});

calculadora/cuadrado.js

define(['calculadora/multiplicar'], function (multiplicar) {
  return function (x) {
      return multiplicar(x, x);
  };
});

Require.js: Modo Avanzado

Require.js con dependencias externas

// vendor/jquery.min.js
require(['vendor/jquery'], function($){
    $('#hola').html("<h1>HOLA! Hola!</h1>");
});

Generando varios modulos en el mismo archivo

script.js

require(['hola', 'adios'], function(hola, adios){
    $('#hola').html("<h1>"+hola+" y "+adios+"!</h1>");
});

hola.js

define(function() {
    return "Hola";
});

define('adios', function() {
    return "adios";
});

configurando baseUrl

Estructura del proyecto:

www/
    /assets/
    /css/
    /js/
        /App/
            main.js
    /vendor/
        bootstrap.js
        jquery.js

config.js

requirejs.config({
    baseUrl: '.assets/js'
});

*.js

require(['vendor/jquery', 'vendor/bootstrap', 'app/main']);

configurando Paths

Estructura del proyecto:

www/
    /assets/
    /css/
    /js/
        /app/
            main.js
    /vendor/
        bootstrap.js
        jquery.js

config.js

requirejs.config({
    baseUrl: '.assets/js',
    paths: {
        'jquery': 'vendor/jquery',
        'bootstrap': 'vendor/bootstrap',
        'main': 'app/main'
    }
});

*.js

require(['jquery', 'bootstrap', 'main']);

Ejercicios

1 - Modulariza tu proyecto usando algún sistema de gestión de dependencias (AMD, Common, ES6)

// Tu solución