RSS Feed

RequireJS: Módulos y Dependencias

6

febrero 24, 2012 by - @pjnovas

Así como tenemos Módulos en NodeJS, también los podemos tener en el cliente usando RequireJS.
RequireJS es una librería basada en la especificación de CommonJS para Módulos la cual nos trae una implementación para cargar módulos del lado del cliente. Esto nos permite mantener nuestro código limpio y en una suerte de “carga en demanda”, ya que se pueden definir dependencias.

También tiene optimizaciones para los scripts, por ej. unificación y compresión para el deploy (por el momento solo para NodeJS y Java).

Veamos un ejemplo simple de como utilizarla y que en que nos ayuda:

root/
  index.html
  scripts/
    main.js
    utils.js
    pepe.js
<!DOCTYPE html>
<html>
  <head>
    <title>Sin RequireJS</title>
    <!-- agrego mi coqueto script main.js-->
    <script type="text/javascript" src="scripts/main.js"></script>

    <!-- main.js tiene una dependencia a utils.js, asi que lo agrego -->
    <script type="text/javascript" src="scripts/utils.js"></script>

    <!-- Pero utils.js tiene una dependencia a pepe.js, lo agrego -->
    <script type="text/javascript" src="scripts/pepe.js"></script>

    <!-- Espero no olvidarme de otro script porque explota en colores -->
  </head>
  <body>
  </body>
</html>

Lo anterior está un poquito inflado, pero es algo bastante común. Por lo general tenemos un layout o master-page donde metemos la mayoria de scripts, pero asi y todo, tenemos que pensar en dependencias, que scripts antes de cual, etc… (en el caso anterior debería cambiar el orden exactamente alreves :P)

Con RequireJS sería algo de este estilo:

Agregamos a nuestro directorio require.js

root/
  index.html
  scripts/
    require.js
    main.js
    utils.js
    pepe.js
<!DOCTYPE html>
<html>
  <head>
    <title>Con RequireJS</title>
    <!-- agrego la referencia a require.js y con data-main 
     le digo que quiero que cargue cuando termine require.js -->
    <script data-main="scripts/main" src="scripts/require.js"></script>
  </head>
  <body>
  </body>
</html>
// le digo a requireJS que voy a necesitar util.js para trabjar en main
require(["util"], function(util) {
  // este callback se dispara cuando util.js fue cargado, pero no 
  // solo util.js, sino tambien cuando sus dependencias se cargaron.
}
// defino util.js como módulo y que tiene una dependencia a pepe.js,
// también puedo especificar mas de una dependencia, por ej jose.js.
define(["pepe", "jose"], function(pepe, jose) {
   return {
     color: 'azul'
   };
}
// defino pepe.js como módulo
define(function() {
  //puedo ejecutar alguna configuracion inicial del módulo,
  // y retornar sus accesos con un simple objeto
  return {
    getAlgo: function(){
      return "algo";
    }    
  };   
}

Bastante mas ordenado, no?. Lo interesante es que no necesito preocuparme en el html de las dependendencias que tiene cada script, ni el orden, ni siquiera acordarme de agregar todos los utilizados. Simplemente referencio en cada script sus dependencias y RequireJS se encarga de lo restante.

Para jQuery varia mínimamente su uso, si están utilizando jQuery vean la implementación aca

Links relacionados:


  • Guest

    Aún no entiendo la diferencia entre require y define, el módulo como actúa en general? Me podrías ayudar a entenderlo porfvor :)

    • pjnovas

      El objetivo de los módulos es agrupar funcionalidades, ordenar el código, apuntando a cortar con el scripting y tener muchas funciones dando vueltas por todos lados. Qué funcionalidad en qué módulo queda a tu criterio, el de la aplicación, patrones de diseño, etc.

      “define” es una función que recibe otra función por parámetro, esa función que envias por parámetro es el módulo que estás definiendo. Siguiendo con la especificación de RequireJS: 1 Módulo – 1 Archivo, vas a tener un define por archivo con el contenido del modulo dentro de la función. (ej util.js, pepe.js)

      “require” es la referencia a un módulo, lo que significa que vas a usar ese módulo para una determinada función. (ej. main.js)

      Lo que puede confundir es que define tiene un parámetro opcional con un arreglo de nombres de módulos de los que depende, pero cuando no sepas cual usar pensá primero que estas haciendo, si vas a definir un módulo es sin vueltas “define”.

      En el ejemplo del post el único que tiene el “require” es el main, lo que significa que la función anónima solo puede ser ejecutada una vez que esos módulos estén listos, ya que los necesito para lo que tenga que hacer.

      Otra forma de pensarlo es … “primero defino un módulo (define), después lo referencio y utilizo (require)”

      Saludos!

  • Elizabeth

    Gracias, muy bien explicado tu articulo.

  • Pablo

    Muy bien explicado el artículo.

    Creo que esto se podría implementar al bundle HTML5 Boilerplate a twitter bootstrap y quizás sería una alternativa para eliminar la tediosa tarea de agregar scripts en las plantillas de wordpress mediante php.

    Como muchos sabemos, para optimizar la velocidad de carga de un sitio web, lo mejor es minificar y unificar código, osea, eliminar todo lo que no sea código de cada fichero js, y unificar todos los js en un solo fichero. De hecho, muchas veces se utiliza una dependencia una vez cargado el sitio web.

    Ahora mi pregunta es.

    ¿¿Esta herramienta sería una buena práctica teniendo esto en cuenta, o es solo para aligerar y mejorar la calidad del código así como su lectura??

  • Pingback: Luke