09 noviembre, 2009

Herramientas de Google Closure

He traducido el artículo de presentación y otros de la documentación sobre las nuevas herramientas presentadas por Google hace unos días:

Millones de usuarios de Google en todo el mundo usan aplicaciones JavaScript intensivas, como Gmail, Google Docs y Google Maps. Al igual que los desarrolladores de todo el mundo, los Googlers (empleados de Google) queremos grandes aplicaciones web que sean más fáciles de crear, por lo que hemos construido unas cuantas herramientas para ayudarnos a desarrollar estas aplicaciones (y muchas otras). Estamos contentos de anunciar la publicación en código abierto de estas herramientas, y estamos orgullosos de ponerlos a disposición de la comunidad de desarrollo web.

El compilador de Closure


El compilador de Closure es un optimizador de JavaScript que compila aplicaciones web en código JavaScript compacto y de alto rendimiento. El compilador elimina el código muerto, a continuación, reescribe y minimiza lo que queda para que se ejecute rápido en los motores de JavaScript de los navegadores. El compilador también comprueba la sintaxis, las referencias a variables, y los tipos, y advierte sobre otros problemas comunes de JavaScript. Estos controles y optimizaciones ayudan a escribir aplicaciones más depuradas y fáciles de mantener. Puedes utilizar el compilador con el inspector de Closure, una extensión de Firebug que hace que la depuración del código ofuscado casi tan fácil como la depuración de código legible por humanos.

Los beneficios de usar el compilador son:
  • Eficiencia. El compilador reduce el tamaño de los archivos JavaScript y al mismo tiempo los hace más eficientes, ayudando a que las aplicaciones carguen más rápido y necesiten menos ancho de banda.
  • Comprobación de código. El compilador genera avisos sobre JavaScript ilega y sobre operaciones potencialmente peligrosas, ayudándote a producir JavaScript más depurado y fácil de mantener.

Debido a que los desarrolladores de JavaScript son un grupo diverso, hemos creado una serie de formas distintas de ejecutar el compilador de Clausure. Hemos publicado a fuente abierta una herramienta de línea de comandos. Hemos creado una aplicación web que acepta código para compilación a través de un cuadro de texto o de una API REST. También estamos ofreciendo una extensión de Firefox que puede utilizar con Page Speed para ver convenientemente los beneficios en rendimiento de las páginas web.

Para probar el compilador:
  1. Descarga el paquete del compilador de Closure.

    Crea un directorio llamado closure-compiler.

    Descarga el compilador del archivo compiler.jar y guárdalo en el directorio closure-compiler.

  2. Crea un archivo JavaScript

    Crea un archivo llamado hello.js que contenga el siguiente código:


    // Una función sencilla.
    function hello(longName) {
      alert('Hola, ' + longName);
    }
    hello('Nuevo usuario');

    Guárdalo en el directorio closure-compiler.


  3. Compila el archivo JavaScript.

    Ejecuta el siguiente comando desde el directorio closure-compiler:

    java -jar compiler.jar --js hello.js --js_output_file hello-compiled.js

    Este comando crea un nuevo archivo llamado hello-compiled.js, que contiene el siguiente JavaScript:


    function hello(a){alert("Hola, "+a)}hello("Nuevo usuario");
    

    El compilador ha eliminado los comentarios, el espacio en blanco y un punto y coma innecesario. El compilador ha reemplazado el nombre de parámetro longName por el más corto a. Esto genera un archivo mucho más pequeño que el original.

    Para confirmar que el código JavaScript compilado aún funciona, incluye hello-compiled.js en un archivo HTML como este:


    <html>
      <head>
        <script src="closure-library-read-only/closure/goog/base.js"></script>
        <script src="hello.js"></script>
      </head>
      <body onload="sayHi()">
      </body>
    </html>
    

    Ahora carga el HTML en el navegador y deberías ver una bienvenida amigable. Para aprender más visita este documento sobre compilación avanzada y con archivos externos.


El inspector de Closure


El compilador de Closure modifica el código JavaScript original y genera código más pequeño y más eficiente, pero más difícil de leer y depurar. El inspector ayuda ofreciendo un mapeo del código fuente que identifica las líneas del código original y el compilado. De esa forma es posible ver el código original en tu editor preferido indicado en la variable de entorno $EDITOR.

Sigue estos pasos para habilitar el mapeo de código fuente al usar el inspector:
  1. Instala Firebug y el inspector.
     
  2. Compila tu código JavaScript con el compilador de Closure y añade el parámetro --create_source_map. Por ejemplo, si tu archivo JavaScript se llama example.js, ejecuta este comando desde el directorio closure-compiler para crear un mapa de código fuente en el fichero example-map:


    $ java -jar compiler.jar --js example.js --create_source_map ./example-map --js_output_file example-compiled.js


  3. Para depurar tu código en Firebug, abre una página web en Firefox que incluya example.js. Abre una ventana de Firebug y carga el archivo JavaScript compilado example-compiled.js en el panel izquierdo. Selecciona la pestaña Source Mapping del panel derecho y especifica la localización del archivo de mapa de código fuente example-map introduciendo la URL o haciendo clic en Open Local File y seleccionándolo.


    Screenshot of Closure Inspector source map dialog.


  4. Tras cargar el mapa de código fuente, puedes encontrar el código original de cualquier código en los fuentes compilados. Para hacer esto, pulsa el botón derecho del ratón sobre cualquier código compilado y selecciona Show Original Source, tal y como se muestra a continuación:


    Screenshot of Closure Inspector: show original source.

    Si has indicado la variable de entorno $EDITOR al editor de tu elección, el archivo original se abre en ese editor en la línea que corresponde a la que has hecho el click. Si no lo has indicado, verás el nombre de fichero y el número de línea mostrados en un pop-up, así:


    Screenshot of Closure Inspector pop-up with original source file 
information.

Traza mejorada de pila

Firebug incluye una representación de la traza de pila que puedes ver pulsando  Stack en la parte derecha de la ventana.
El inspector de Closure mejora la representación de la traza de pila mediante:
  • Mostrando los nombres de función originales si se ha cargado el mapa del código fuente.
  • Mostrando el archivo que contiene cada función.
  • Facilitando un botón  Copy Stack de forma que se pueda copiar el texto de la traza de pila.
A continuación hay un ejemplo de traza mejorada de pila del inspector Closure:

Screenshot of Closure Inspector's enchanced stack trace.

Integración de Tests Unitarios

El inspector de Closure soporta la integración con el Framework de test de Closure. Para habilitar esta característica, pulsa la flecha en el menú Script de Firebug y habilita Handle JSUnit Failures. Si una aserción falla en el Framework de testeo, el Inspector de Closure establece un punto de ruptura sobre la aserción y pausa la ejecución.

Aquí hay un ejemplo de punto de ruptura sobre una aserción:

Screenshot of Closure Inspector assertion breakpoint.

La Biblioteca de Closure


La biblioteca de Closure es una amplia colección de código JavaScript modular, bien probada y multi-navegador. Los desarrolladores Web pueden coger solamente lo que necesiten de un amplio conjunto de widgets reutilizables y controles de interfície de usuario, así como de utilidades de bajo nivel para el DOM, comunicación con el servidor, animación, estructuras de datos, pruebas unitarias, edición de texto rico, y mucho, mucho más. (De verdad. Echa un vistazo a la documentación.)

JavaScript carece de una biblioteca de clases estándar como STL o JDK. En Google, la biblioteca de Closure hace el papel de "nuestra biblioteca estándar de JavaScript" para la creación de grandes aplicaciones web complejas. Es intencionadamente agnóstica en cuanto a servidor y destinada para ser usada con el compilador de Closure. Puedes hacer un proyecto grande y complejo (con espacios de nombres y comprobación de tipos), y aún así, pequeño y rápido a través del cable (con compilación). La Biblioteca de Closure proporciona utilidades para tareas comunes de manera que pases el tiempo escribiendo la aplicación en lugar de escribir utilidades y abstracciones de navegador.

Si estás desarrollando una aplicación grande o creciente, podrías beneficiarte de la amplitud de la biblioteca. Una biblioteca bien probada puede aislarte de los problemas de compatibilidad entre navegadores y ahorrar tiempo de programación en el lado del cliente, permitiendo que te dediques a la parte divertida.


Empezando con la biblioteca de Closure

Este ejercicio de "Hola Mundo" te introduce en el proceso de usar la biblioteca en una página web. Para hacer este ejercicio necesitas estar familiarizado con JavaScript, y con un cliente de Subversion. Podrías tener ya uno. Lo sabrás intentando ejecutar el comando del paso 1.

Hola Closure

Para empezar con la librería Closure, utiliza funciones JavaScript de Closure en una página web simple siguiendo estos pasos:

Paso 1: Descargar la biblioteca

Descarga la biblioteca de Closure desde el repositorio de Subversion ejecutando siguiente comando desde consola:

svn checkout http://closure-library.googlecode.com/svn/trunk/ closure-library-read-only

Podrías necesitar un cliente de Subversion para ejecutar el comando, aunque podrías tener ya uno. Prueba el comando, y si no funciona, descarga e instala un  cliente de Subversion.
Tras ejecutar el comando deberías tener un directorio llamado closure-library-read-only que contenga el código de la librería.

Paso 2: Crear un archivo JavaScript que use la biblioteca de Closure

Guarda el siguiente JavaScript en un archivo llamado hello.js. Pon este archivo junto al directorio  closure-library-read-only.

goog.require('goog.dom');

function sayHi() {
  var newDiv = goog.dom.createDom('h1', {'style': 'background-color:#EEE'},
    'Hola Mundo!');
  goog.dom.appendChild(document.body, newDiv);
}

Paso 3: Crea un archivo HTML

Guarda el siguiente HTML en un archivo llamado hello.html. Pone este archivo junto al directorio closure-library-read-only y el archivo hello.js.

<html>
  <head>
    <script src="></script>
    <script src="hello.js"></script>
  </head>
  <body onload="sayHi()">
  </body>
</html>

Paso 4: Dí Hola a la biblioteca

Abre el archivo HTML en el navegador. Deberías de ver las palabras "Hello world!":



¿Cómo funciona el ejemplo?

El JavaScript de hello.js usa dos funciones que no define: goog.dom.createDom() y goog.dom.appendChild(). ¿De dónde vienen esas funciones?

Esas funciones están definidas en la biblioteca de Closure que descargaste en el paso 1, dentro del archivo  closure-library-read-only/dom/dom.js.
Para hacer uso de esas funciones, el ejemplo hace dos cosas:
  • Incluye la orden goog.require('goog.dom') al principio del JavaScript del paso 2.
  • Incluye el archivo de arranque de la biblioteca  base.js en el HTML del paso 3.
El archivo base.js define la función  goog.require(). La llamada de función goog.require('goog.dom') carga el archivo JavaScript que define las funciones del espacio de nombres goog.dom, junto con otros archivos de la biblioteca de Closure que esas funciones necesitan.
La biblioteca de Closure carga esos archivos agregando dinámicamente una etiqueta script al documento por cada archivo necesitado de la biblioteca. Así, por ejemplo, la orden  goog.require('goog.dom') hace que la siguiente etiqueta sea agregada al document, donde path-to-closure es el camino desde el archivo HTML hasta el directorio que contiene base.js:
Normalmente una sola orden goog.require() cargará sólo una fracción de la base de código de la biblioteca.
Incluir base.js no es la única forma de incluir el código de la librería, pero es la forma más fácil de empezar. En cualquier caso, no importa como cargues el código de la librería, siempre usarás goog.require() para declarar las partes de la librería que necesitas.

Construir una aplicación con la biblioteca de Closure

Este otro tutorial te introduce en la experiencia de usar la biblioteca dando un paseo por la construcción de una aplicación simple.
Usa estos enlaces para descargar los dos archivos de código fuente usados en el tutorial:
Este tutorial explica las diferentes partes de estos archivos paso a paso. Fíjate que el archivo notepad.html no funcionará hasta que no descargues y enlaces tu copia de la biblioteca, tal y como se describe a continuación.

La aplicación de Notas

Este tutorial ilustra el proceso de construir una simple aplicación para mostrar notas. El ejemplo:
  • crea un espacio de nombres para la aplicación,
  • usa la función de la biblioteca goog.dom.createDom() para crear la estructura de Document Object Model (DOM) para la lista, y
  • usa una clase de la biblioteca en la lista de notas para permitir al usuario abrir y cerrar elementos de la lista.

Creando un espacio de nombres con goog.provide()

Cuando usas bibliotecas JavaScript de fuentes distintas, siempre existe la posibilidad de que algún archivo Javascript redefina una variable global o un nombre de función que ya uses en tu código. Para minimizar el riesgo de este tipo de colisión de nombres, usa la función goog.provide() de la librería para crear un espacio de nombres para tu código.


Por ejemplo, la aplicación de Notas usa objetos Note creados por una función constructor Note(). Si cualquier otro archivo JavaScript define una función global o una variable Note, sobreescribiría al constructor. Por tanto, el ejemplo crea un espacio de nombres para este constructor con la siguiente llamada a goog.provide:



goog.provide('tutorial.notepad.Note');


La función goog.provide() se asegura de la existencia de la estructura de objeto JavaScript indicada por su argumento. Comprueba si existe cada propiedad de objeto en la ruta de la expresión y si no existe la inicializa. La función anterior es equivalente a:

tutorial = tutorial || {};
tutorial.notepad = tutorial.notepad || {};
tutorial.notepad.Note = tutorial.notepad.Note || {};


Puesto que goog.provide() sólo inicializa propiedades si no existen previamente, nunca sobreescribirá una propiedad.
Fíjate que las órdenes goog.provide() tiene la ventaja añadida de que el script de resolución de dependencias calcdeps.py puede usarlas. Echa un vistazo a Usar el script de cálculo de dependencias para aprender cómo usar calcdeps.py

Una vez que la estructura de objeto tutorial.notepad.Note exista, el ejemplo asigna la función constructor a la propiedad Note.


tutorial.notepad.Note = function(title, content, noteContainer) {
  this.title = title;
  this.content = content;
  this.parent = noteContainer;
};

El constructor Note está ahora en el espacio de nombre tutorial.notepad creado con goog.provide().


Creando una estructura DOM con goog.dom.createDom()

Para mostrar una Note en el documento HTML, el ejemplo facilita a la clase Note el siguiente método:



tutorial.notepad.Note.prototype.makeNoteDom = function() {
  // Crea la estructura DOM que representa la nota.
  this.headerElement = goog.dom.createDom('div', 
      {'style': 'background-color:#EEE'}, this.title);
  this.contentElement = goog.dom.createDom('div', null, this.content);
  var newNote = goog.dom.createDom('div', null,
      this.headerElement, this.contentElement);

  // Agrega la estructura DOM de la nota al documento.
  goog.dom.appendChild(this.parent, newNote);
};


Este método usa la función de la biblioteca goog.dom.createDom(). La siguiente orden goog.require() incluye el código para esta función:



goog.require('goog.dom');


Para incluir una función como goog.dom.createDom() que no sea un constructor, pasa el espacio de nombres que contenga la función a goog.require (en este caso solamente goog.dom). No necesitas incluir el nombre de la función en la orden goog.require() a menos que requieras de una clase. Usando una clase de la biblioteca ilustra una órden goog.require() para este caso.


La función goog.dom.createDom() crea un nuevo elemento DOM. Por ejemplo, la siguiente órden de makeNoteDom() crea un nuevo elemento  div.



this.headerElement = goog.dom.createDom('div', 
      {'style': 'background-color:#EEE'}, this.title);


El segundo argumento en esta llamada a createDom() especifica los atributos a agregar al elemento, y el tercer argumento especifica un hijo que añadir al elemento (una cadena en este caso). Ambos, el segundo y el tercer parámetro, son opcionales. Mira la referencia de la API para más información sobre createDom().


El método makeNoteDom() simplemente crea un argumento de una sola Note. Para crear una lista de notas, el ejemplo incluye una función makeNotes() que toma un array de  de datos de notas e instancia un objeto Note por cada uno, llamando al método makeNoteDom() de Note.



tutorial.notepad.makeNotes = function(data, noteContainer) {
  var notes = [];
  for (var i = 0; i < data.length; i++) {
    var note = 
      new tutorial.notepad.Note(data[i].title, data[i].content, noteContainer);
    notes.push(note);
    note.makeNoteDom();
  }
  return notes;
};

Usando una clase de la biblioteca

Con sólo dos líneas de código, el ejemplo hace de cada nota un Zippy. Un Zippy es un elemento que puede ser plegado y desplegado para ocultar o mostrar contenido. Primero el ejemplo agrega una nueva orden require() para la clase Zippy:
goog.require('goog.ui.Zippy');
Entonces añade una línea al final del método  makeNoteDom :
tutorial.notepad.Note.prototype.makeNoteDom = function() {
  // Crea la estructura DOM para representar la nota.
  this.headerElement = goog.dom.createDom('div', 
      {'style': 'background-color:#EEE'}, this.title);
  this.contentElement = goog.dom.createDom('div', null, this.content);
  var newNote = goog.dom.createDom('div', null,
      this.headerElement, this.contentElement);

  // Agrega la estructura DOM de la nota al documento.
  goog.dom.appendChild(this.parent, newNote);

  // NUEVA LINEA:
  return new goog.ui.Zippy(this.headerElement, this.contentElement);
};
La llamada al constructor new goog.ui.Zippy(this.headerElement, this.contentElement) asigna un comportamiento al elemento nota que conmutará la visibilidad de  contentElement cuando el usuario haga clic sobre  headerElement. Para más información sobre la clase Zippy, echa un vistazo a la documentación de la API de Zippy.

Usando el Notepad en un documento HTML

A continuación se puede ver el código JavaScript completo para esta aplicación de ejemplo:
goog.provide('tutorial.notepad');
goog.provide('tutorial.notepad.Note');

goog.require('goog.dom');
goog.require('goog.ui.Zippy');

/**
 * Itera sobre una lista de objetos de datos de nota, crea una instancia de Nota
 * para cada uno, y le dice a la instancia que construya su estructura DOM.
 */
tutorial.notepad.makeNotes = function(data, noteContainer) {
  var notes = [];
  for (var i = 0; i < data.length; i++) {
    var note = 
      new tutorial.notepad.Note(data[i].title, data[i].content, noteContainer);
    notes.push(note);
    note.makeNoteDom();
  }
  return notes;
};

/**
 * Gestiona los datos e interfície de un solo nodo
 */
tutorial.notepad.Note = function(title, content, noteContainer) {
  this.title = title;
  this.content = content;
  this.parent = noteContainer;
};

/**
 * Crea la estructura DOM para la nota y la agrega al documento.
 */
tutorial.notepad.Note.prototype.makeNoteDom = function() {
  // Create DOM structure to represent the note.
  this.headerElement = goog.dom.createDom('div',
      {'style': 'background-color:#EEE'}, this.title);
  this.contentElement = goog.dom.createDom('div', null, this.content);
  var newNote = goog.dom.createDom('div', null,
      this.headerElement, this.contentElement);

  // Agrega la estructura DOM de la nota al documento
  goog.dom.appendChild(this.parent, newNote);
  return new goog.ui.Zippy(this.headerElement, this.contentElement);
};
Puedes tener este código descargando el archivo notepad.js. El siguiente HTML usa este código de bloc de notas para mostrar una lista de notas en una página web:
<html>
<head>
<title>Notepad</title>
<script src="closure-library/base.js"></script>
<script src="notepad.js"></script>
</head>
<body>

<div id="notes">
</div>

<script>
function main() {
  var noteData = [
    {'title': 'Note 1', 'content': 'Content of Note 1'},
    {'title': 'Note 2', 'content': 'Content of Note 2'}];

  var noteListElement = document.getElementById('notes');
  var notes = tutorial.notepad.makeNotes(noteData, noteListElement);
}
main();
</script>
</body>
</html>
Puedes tener esta página descargando el archivo notepad.html, Esta página:
  • incluye una etiqueta script para el archivo base.js de la biblioteca. El atributo src de esta etiqueta es el camino desde el archivo HTML hasta el archivo  base.js de la biblioteca.
  • incluye una etiqueta  script para el archivo que contiene el código para el bloc de notas.
  • incluye un script en la página que inicializa la lista de notas con una llamada a makeNotes(). La función makeNotes() toma dos argumentos: un array de Objetos, cada uno conteniendo los datos de una nota, y el elemento DOM bajo el que construir la estructura DOM de la lista de notas

Plantillas de Closure

Las plantillas de Closure surgieron de un deseo de tener plantillas web que estén precompiladas para ser ejecutadas de forma eficiente por JavaScript. Las plantillas de Closure tienen una sintaxis simple y natural para programadores. A diferencia de los tradicionales sistemas de plantillas, puedes pensar en las plantillas de Closure como pequeños componentes que se unen para formar una interfície de usuario, en lugar de tener que crear una gran plantilla por cada página. Las plantillas de Closure se aplican para JavaScript y para Java, así que puedes usar las mismas plantillas en el servidor y en el cliente. El compilador, la biblioteca, las plantillas y el inspector de Closure se iniciaron como proyectos de 20% del tiempo de Googlers y cientos de ellos han contribuido con miles de parches. Hoy en día, cada herramienta de Closure se ha convertido en una parte clave de la infraestructura de JavaScript detrás de las aplicaciones web en Google. Por eso estamos particularmente entusiasmados de abrir su código para fomentar y apoyar el desarrollo web fuera de Google. Queremos saber lo que piensas, pero más importante aún, queremos ver que podéis hacer con ellas. Así que bájatelo y diviértete!



Addendum: Parece que Andrea Giammarchi no está muy de acuerdo con las ventajas ofrecidas por el nuevo proyecto de Google. Ha encontrado fácilmente un ejemplo en el que el compilador hace que el código deje de funcionar (aunque hay que decir que hace mal uso de las variables globales que un buen estilo de programación debería evitar). Por otro lado analiza el código de la librería y encuentra distintos problemas de rendimiento que una librería orientada a la velocidad no debería tener. Quizás tenga razón y todo se deba a que detrás de esas herramientas hay programadores expertos en Java y no en JavaScript...

Publicar un comentario en la entrada

Últimos links en indiza.com