Una sinfonía en C#

Un pequeño aporte a la comunidad de habla hispana.

Manejando dependencias en Javascript con RequireJs

A medida que nuestra aplicación crece en número de archivos Javascript y, más aún, si nuestra aplicación es una “single page application” (es decir todo ocurre en el mismo HTML y se va cargando y descargando contenido) y, por supuesto, usamos módulos para separar la aplicación en partes lógicas, empieza a ser complicado manejar algunos problemas como:

  1. Referencias que se cargan tarde
  2. Referencias que se cargan más de una vez
  3. Referencias que se cargan y no se utilizan

Es decir, nuestro proyecto tiene un tamaño interesante, unas cuantas referencias (bibliotecas, funcionalidad de diferentes partes de la pantalla, etc.) y nos encontramos con problemas de este tipo:

  1. Errores por referencias no cargadas
  2. Sobre-escritura de métodos por librerías que se cargan más de una vez, por ejemplo un plugin de jQuery que deja de andar cuando se carga jQuery una vez que cargó el plugin.
  3. Lentitud de la aplicación por esperar cargar cosas innecesarias o que aún no se necesitan, por ejemplo si ponemos todo en el la sección header del html para que se cargue siempre y evitar el problema 1.
  4. Crecimiento del costo de ancho de banda y peticiones al servidor para cargar referencias que no siempre se usan, imaginen que nuestra aplicación tiene una sección de carga de pedidos con un montón de validaciones pero no siempre se activa esa parte, cargar todo el código relativo a esa funcionalidad siempre no es lo más inteligente.
  5. etc.

RequireJs, una biblioteca para manejar dependencias en Javascript

Esta pequeña biblioteca nos ayuda solucionar estos problemas, vamos a enumerar alguna de sus características:

  • Carga dinámica de dependencias
  • Descarga automáticas de dependencias
  • Manejo de timeouts y carga de fallbacks

Adicionalmente podemos, gracias a una de sus utilidades logar

  • Minificación
  • Bundling

Primer ejemplo con Requirejs

Empecemos por lo más simple, creamos un HTML y agregamos la referencia a requirejs

<html>
<head>
<script type="text/javascript" src="http://requirejs.org/docs/release/2.1.9/minified/require.js"></script>
</head>
<body></body>
</html>

Primero indicamos a requirejs dónde empezar

Bien, este tag script va a ser la única referencia que vamos a tener, de todo lo demás se va a encargar requirejs, para eso tenemos que decirle cuál es el archivo por el cual va a comenzar nuestra aplicación, simplemente agregamos al mismo tag script un atributo

<script type="text/javascript" data-main="main" src="http://requirejs.org/docs/release/2.1.9/minified/require.js"></script>

con el atributo “data-main” le decimos a requirejs qué archivo es el punto de entrada de nuestra aplicación, como verán en este caso es “main.js” (el js siempre se omite), ahora dentro de nuestro archivo main.js hacemos lo siguiente:

require([], function(){
	console.log("hola require");
});

Bien, si lo ejecutamos imprime en la consola del navegador, vamos a explicar qué es esto

La función require

Esta función la vamos a usar cuando queramos cargar un módulo, el primer parámetro (que ahora es un array vacío) indica la dependencias, es decir, de qué otro módulos depende éste para correr, por ahora nada, el segundo parámetro es un callback que se ejecuta cuando todas las dependencias han sido resueltas, es decir si main.js depende de a.js y éste a su vez de b.js requirejs se va a encargar de cargar estas dependencias y asegurarse que están disponibles antes de llamar al callback (incluso si ya están cargadas no lo hace nuevamente) excelente, vamos a verlo.

main.js

require(["a"], function(){
	console.log("soy main");
});

a.js

define(["b"], function(){
	console.log("soy a");
});

En lugar de usar require() usamos define() ya que estamos definiendo un módulo, no pidiendo que se cargue, de eso se va a encargar requirejs cuando resuelva las dependencias.

b.js

define([], function(){
	console.log("soy b");
});

Ejecutamos el código y vemos el orden de resolución de dependencias

image

Utilizando dependencia

Hasta ahora comprobamos el orden de carga y no mucho más, lo interesante es tener funcionalidad que se cargan y poder utilizar los objetos que define cada módulo, bien, para eso cambiamos un poco el ejemplo:

main.js

require(["perro"], function(Perro){
	var roger = new Perro();
	roger.ladrar();
});

perro.js

define([], function(){
	var Perro = function(){
	};
	
	Perro.prototype.ladrar = function(){
		console.log("guauu!!");
	};
	
	return Perro;
});

lo corremos y…

image

Básicamente en main.js definimos la dependencia y luego la recibimos como parámetro del callback, dentro de perro.js tenemos que hacer return del objeto que queremos que el módulo reciba.

Cargando bibliotecas externas con Requirejs

Por supuesto que podemos cargar dependencias de terceros, la primera que viene a la mente es jQuery, pero como no es práctico copiarnos en código dentro de un módulo lo que hacemos es configurar requirejs para que cuando alguien le pida jQuery sepa qué hacer, del siguiente modo:

//configuramos requirejs para que cuando alguien pida jquery cargue la dependencia
requirejs.config({
	"paths": {
		"jquery": "http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min",
	}
});

require(["perro", "jquery"], function(Perro, $){
	var roger = new Perro();
	var saludo = roger.ladrar();
	$("body").append("<strong>" + saludo + "</strong>");
});

(modifiqué el método ladrar para que retorne un saludo)

image

básicamente llamamos al método config de requirejs y le decime qué hacer cuando le pidan jQuery.

jQuery soporta requirejs

Como muchas otras bibliotecas populares jQuery está preparada para soporta requirejs, de qué forma? bueno, si miramos el código jQuery podemos descubrirlo

	if ( typeof define === "function" && define.amd ) {
		define( "jquery", [], function () { return jQuery; } );
	}

Como vemos al final del código de jQuery esta línea se fija si existe requirejs y si es así define un módulo llamado jquery (utiliza otra sobrecarga del método define para ello)

Bien, es todo por esta vez, hay mucho más para ver, como definir dependencias entre bibliotecas de terceros y demás, pero lo dejamos para la próxima. Enjoy.

Comments (1) -

Loading