viernes, 6 de septiembre de 2013

Como optimizar módulos de RequireJS y archivos Javascript (II)

Require.js
Aunque pueda parecer que no y en muchos casos no se tenga en cuenta la diferencia que puede haber entre una aplicación web no optimizada y optimizada puede ser significativa en varios aspectos.

Marionette
¿Por que en algunos casos es importante optimizar la aplicación? Uno de los motivos es conseguir una mejor experiencia de usuario haciendo que la página le cargue más rápido. Una página que tarde en cargar demasiado puede significar pérdida de visitas y si se trata de una página de comercio electrónico de clientes y compras. Si un flujo importante de usuarios de una página procede de las búsquedas la velocidad de carga de la página es importante ya que es una de las variables que tiene muy en cuenta el algoritmo de Google para establecer el ranking de los resultados, reducir el tiempo de carga puede significar aparecer antes en los resultados de la búsqueda y esto significa más clics en nuestro resultado, más visitas y nuevamente más potenciales clientes y compras. Y en caso de que no estemos desarrollando una aplicación accesible desde internet sino una aplicación para una empresa o administración publica hacer que cargue más rápido puede suponer una mayor satisfacción de los usuarios y una aplicación más eficiente (con una menor carga para el servidor, con la posibilidad de soportar más usuarios o con menor necesidad de hardware).

La velocidad de carga de una página se ve afectada por varios elementos entre ellos el número de peticiones que hace la página para recuperar los recursos (imáges, css, fuentes, javascript) y el peso de esos archivos. Cuantas menos peticiones hagamos y menos peso de los archivos notaremos una significativa reducción de tiempo de carga de la aplicación. El tamaño de los archivos no afecta tanto a dispositivos que acceden por líneas de banda ancha de varios megas pero hay muchos dispositivos móviles como los teléfonos inteligentes que hace uso de conexiones de datos móviles, las velocidades son más modestas en estos y muchos usuarios tienen cuotas de megas descargados, es de agradecer tenerlos en cuenta también.

En esta entrada usaré el Ejemplo de la lista de tareas con Marionette, un ejemplo sencillo pero bastante completo que usa RequireJS, Backbone y Marionette, jQuery, Mustache, el plugin i18n de RequireJS para internacionalizar los textos de las plantillas, el plugin text para externalizar del html las plantillas de Mustache. Me centraré en como optimizar toda esa cantidad importante de código javascript que necesita el ejemplo cargar en el navegador del usuario. El estado inicial de la aplicación es de 28 peticiones, un peso de 737 KiB utilizando las versiones no minimizadas de las librerías javascript y un tiempo de carga de 261 ms funcionando en local, si tomásemos medidas de tiempo pero funcionando en internet la latencia haría que el tiempo de carga fuese aún mayor. En la imagen se puden apreciar estos datos en la parte inferior de las herramientas para desarrolladores de Chrome.

Para minimizar los archivos de javascript y reducir su peso y también para agregarlos en unos solo que evite muchas peticiones usaré el optimizador de RequireJS (r.js). Esta utilidad ya la que expliqué en la Optimización de módulos RequireJS y archivos javascript del ejemplo más sencillo de la Introducción a Backbone pero ahora lo aplicaré con este ejemplo de Marionette que es bastante más complejo.

Para usar r.js necesitaremos instalar previamente node.js (pacman -S nodejs en Arch Linux). Después deberemos crear el archivo de configuración para r.js, en el que básicamente indicamos la localización de los archivos a optimizar (baseUrl), el nombre del módulo de la aplicación (main), el archivo de resultado agregado y minimizado (out) y la misma configuración shim que utilizaríamos en la aplicación (shim).

Ejecutando este archivo build.js con r.js a través de node obtendremos el archivo agregado y minimizado main.min.js que será el único archivo de módulo que necesitará cargar nuestra aplicación:



Para usar este nuevo módulo deberemos cambiar el nombre del archivo de main.min.js a main.js o cambiar el nombre del módulo del archivo generado de main a main-min (al final del archivo generado, en el define) si hacemos esto último deberemos especificar este nuevo módulo en el atributo data-main de la etiqueta script que cargar el archivo require.js (en Index.tml). En el archivo parece que a pesar de optimizarse se siguen generando algunos comentarios si queremos optimizarlo completamente podemos eliminarlos. El resultado es que cargando este módulo optimizado ahora la aplicación hará tan solo 14 peticiones, la mitad de peticiones, necesitará descargar 403 KiB y se cargará en 245 ms. La diferencia de tiempo no es significativa por hacer las pruebas en local, como decía la latencia de internet o el ancho de banda de un móvil probablemente haría que el tiempo fuese bastante mayor.

Finalmente si queremos automatizar este comando con Gradle podemos hacerlo añadiendo lo siguiente en nuestro archivo de construcción:

Optimizar el javascript y los módulos de RequireJS solo es una de las cosas que podemos hacer para optimizar nuestra aplicación, hay otras muchas cosas que podemos hacer como se comentan en los siguientes enlaces, además si es el caso también deberíamos tener en cuenta el SEO:

https://developers.google.com/speed/
https://developers.google.com/speed/docs/best-practices/rules_intro?hl=nl
http://developers.google.com/speed/pagespeed/insights/
http://guestpostlabs.com/complete-guide-to-website-speed-optimization/
http://zettalab.com.my/website-speed-optimization/

Como en el resto de entradas el código fuente completo lo puedes encontrar en mi repositorio de GitHub. Si quieres probarlo en tu equipo lo puedes hacer de forma muy sencilla con los siguientes comandos y sin instalar nada. Si no dispones de git para clonar mi repositorio de GitHub puedes obtener el código fuente del repositorio en un archivo zip con el anterior enlace. Referencia:
Introducción y ejemplo de Backbone.js
Ejemplo lista de tareas con Backbone, RESTEasy y Tapestry
Optimizar módulos de RequireJS y archivos Javascript