Hero

Qué es y como funciona RequireJS

Abril 27, 2014

enzo
JavaScript

RequireJS es una librería JavaScript que nos permite aislar mediante módulos los componentes de nuestra aplicación cliente y resolver las dependencias de estos mismos.

RequireJS implementa el patron de diseño de software AMD (Asynchronous module definition) lo cual se podría decir es muy similar al Dependency injection implementado por muchos frameworks como Symfony.

A continuación algunas de las características de RequireJS

  • Carga dinámica de dependencias
  • Descarga automáticas de dependencias
  • Manejo de timeouts y carga de fallbacks
  1. Definir punto de entrada para RequireJS.

Debemos bajar la librería de RequireJS disponible en el sitio web http://requirejs.org que ubicaremos un directorio llamado libs para efectos de este ejemplo (probado con la version 2.1.11).

Ahora definiremos en un archivo html que podría ser index.html la carga de RequireJS como se muestra a continuación.


<html>
<head>
</head>
<body>
  <script data-main="libs/main" src="libs/require.js"></script>
</body>
</html>

Como se ve la inclusión se realiza con la etiqueta script de HTML dentro del body, pero también podría hacerse dentro del <head></head>.

También es posible hacer la inclusión de forma remota usando la URL external por ejemplo http://requirejs.org/docs/release/2.1.11/minified/require.js, pero ya queda a gusto de lector.

Como se puede apreciar al incluir RequireJS se hace uso de la etiqueta data-main, esta etiqueta define el punto de entrada para la configuración de RequireJS, el cual es un path de un archivo que escojamos y no es necesario incluir la extensión js y la ruta es relativa.

  1. Configurar RequireJS.

En nuestro archivo libs/main.js definiremos las opciones, lo primero que debemos hacer definir los archivos con los cuales vamos a trabajar como se muestra a continuación.

require.config({
    baseUrl: 'libs',
    paths : {
      backbone : 'backbone',
      underscore : 'underscore',
      jquery : 'jquery',
      marionette : 'backbone.marionette',
      wreqr : 'backbone.wreqr',
      eventbinder : 'backbone.eventbinder',
      babysitter : 'backbone.babysitter'
  } 
});

Todos los archivo se buscaran dentro de directorio definido usando la propiedad baseUrl, y con la propiedad paths se definirán la ubicación de las librerías y se definirá una llave para referirse a la librería posteriormente, la extensión .js no es necesaria y será agregada por RequireJS posteriormente.

Como su nombre lo indica el patron AMD realiza una carga asíncrona de los paquetes. Debido a que no todas la librerias implementan este patron AMD es probable que una librería A intente a usar una función de una librería B que aun no este cargada ocasionando un error.

Para resolver este problema utilizaremos la propiedad Shim de la configuración de RequireJS, como podemos apreciar a continuación.

require.config({,
  shim : {
    jquery : {
      exports : 'jQuery'
    },
    underscore : {
      exports : '_'
    },
    backbone : {
      deps : ['jquery', 'underscore'],
      exports : 'Backbone'
    },
    wreqr: {
      deps : ['backbone']
    },
    eventbinder : {
      deps : ['backbone']
    },
    babysitter : {
      deps: ['backbone']
    },
    marionette : {
      deps: ['wreqr', 'eventbinder', 'babysitter'],
      exports : 'Marionette'
    }
  }
});

La configuración con Shim es un remplazo al plugin Order disponible antes de la version 2.0 de RequireJS. Usando Shim podemos definir un export de variables Globales que podrán ser usadas dentro de otras librerías como es el caso de jquery que exporta la variable jQuery;

La variable exports puede también ser una función anónima que llame el retorno de la función prototipo noConflict en caso de que la librería lo soporte.

Además usando la propiedad deps, es posible definir que librerías deberán ser cargadas antes de incluir la librería que estamos definiendo, como es el caso de backbone que depende de jquery y underscore para poder utilizarse.

Como es lógico si la librería que deseamos usar con RequireJS soporta el patron AMD la configuración con Shim no es necesaria.

  1. Como ejecutar código con require.

Una vez definidos las librerías y sus dependencias si es necesario, ahora podemos reescribir el código que necesitamos como se muestra a continuación.

// Loading dependences and execute Marionette App
require( ["marionette"], function (Marionette) {
    // set up the app instance
    var MyApp = new Marionette.Application();

    MyApp.on("initialize:after", function(){
      alert("Application has started!");
    });

    MyApp.start();
});

El código anterior verifica que todas la librerías necesarias para ejecutar Marionette se carguen antes de ejecutar el código y recibe la variable global exportada como parámetro. En la función anónima se creara una Aplicación Marionette y se inicia, dando como resultado un alert con el mensaje “Application has started”.

Este require tiene el mismo efecto de una función anónima, en el sentido que se ejecutara sin problemas pero no podrá ser utilizada posteriormente.

  1. Como crear módulos nombrados.

Si deseamos encapsular nuestra App debemos hacer módulos nombrados usando la función define y se diferencia del require en que recibe un parámetro que se usa para nombrar el modulo y retorna un objeto o variable que sera utilizada cuando se instancia el modulo. A continuación veamos el ejemplo de punto anterior como un modulo nombrado.

define( 'MyApp', ["marionette"], function (Marionette) {

    // set up the app instance
    var MyApp = new Marionette.Application();

    MyApp.on("initialize:after", function(){
      alert("Application has started!");
    });

    // export the app from this module
    return MyApp;
});

// Fetch and execute Marionette App
require( ["MyApp"], function (MyApp) {
    // Execute App
    MyApp.start();
});

Como se ve el define registra el modulo llamado MyApp y retorna un objeto definido en Marionette, posteriormente con un requiere se carga el modulo, se recibe como parámetros el objeto que retorna el modulo y podemos de esta forma iniciar nuestra aplicación.

Podría parecer una perdida de tiempo hacerlo de esta forma ya que esta en el mismo archivo, pero la idea es crear un modulo por archivo y usando la configuración de require cargar todas las librerías que definan módulos y luego usar estos módulos en nuestra aplicación principal.

Puedes descargar el repositorio Marionette Skeleton App el cual implementa los conceptos aquí explicados.

Espero que haya sido de su agrado.

Recibe consejos y oportunidades de trabajo 100% remotas y en dólares de weKnow Inc.