Alternativas a jQuery

28 Mar 2011

Introducción

No cabe duda de que jQuery está de moda; es la librería favorita para los desarrolladores, con una sintaxis muy amigable y miles de plugins disponibles para casi cualquier necesidad o efecto que podamos imaginar.

Además de todo esto, el equipo del jQuery Team está añadiendo nuevas mejoras a cada versión con el fin de hacerla viable para proyectos de gran envergadura. La última versión hasta la fecha, la 1.5.1, incluye nuevos métodos que apuntan en esta dirección.

Es por todo esto que, cuando comenzamos a desarrollar una aplicación web, solemos casi de forma automática incluir en nuestra estructura de directorios una carpeta JS o Javascript con la versión más reciente de jQuery.

Sin embargo, no siempre nuestros proyectos tienen la complejidad suficiente como para sacar partido a las funcionalidades que una librería como jQuery ofrece. La mayoría de las veces, lo único que necesitamos es seleccionar algún elemento del DOM de una forma sencilla o asociar determinados eventos a botones y campos de un formulario.

¿Qué inconvenientes tiene jQuery?

En estos casos, tendríamos que tener en cuenta algunos aspectos de las librerías que solemos pasar por alto:

  • jQuery, aunque se optimiza en cada versión, supone más de 85 Kbs de carga.
  • Cada vez que utilizamos jQuery para seleccionar un elemento, le estamos asociando sin saberlo un gran número de métodos que esta librería precisa de forma interna para su manipulación.

Estos dos puntos suponen al fin y al cabo un coste de memoria y rendimiento que puede llegar a resultar significativo tanto en aquellas aplicaciones en las que hay un gran número de nodos en el DOM como en las destinadas a dispositivos móviles.

Para demostrar lo anterior, basta con hacer una sencilla prueba: imaginad que queremos coger todos las etiquetas anchor de una página para aplicarles algún efecto.El primer paso lógico sería montar un selector jQuery para despúes operar sobre el resultado.

Bien, pues vamos a abrir la consola de Firebug en una página que utilice esta librería (por ejemplo la oficial) y seleccionar todas las etiquetas anchor:

var anchorCollection = $('a');
console.dir( anchorCollection );

Este sería el paso previo antes de poder aplicar nuestro efecto. Si ojeamos el resultado en la consola, podremos ver (en este caso concreto), que hemos recogido más de 65 elementos a los que se le han aplicado 143 métodos.

Si la única necesidad de nuestra página es la que hemos descrito, podemos fácilmente conlcuir que quizá no está del todo justificado el uso de jQuery: los más de 140 métodos anteriores tienen un coste que, aunque para este caso pueden ser asumibles, en otras aplicaciones (pensemos en una destinada a un teléfono móvil) pueden terminar siendo un lastre difícil de superar.

¿Qué alternativas tenemos entonces para realizar el trabajo duro?

Pues en primer lugar, y como resulta obvio, podemos utilizar Javascript puro, sin librerías. Seleccionar una serie de elementos en una página no tiene más misterio que el de conocer los selectores nativos del lenguaje.

Para el ejemplo anterior, basta con el siguiente código:

var anchorCollection = document.getElementsByTagName( 'a' );
console.dir( anchorCollection );

El resultado en esta ocasión es sustancialmente diferente: tenemos de nuevo los 66 elementos pero ahora no hay métodos asociados. No hay penalizaciones de rendimiento que tener en cuenta.

Sin embargo, en el mundo real, las selecciones son a menudo mucho más complejas. Los selectores de tipo CSS2 y CSS3 que aplican las principales librerías pueden resultar una ayuda extraordinaria en estructuras muy complejas y anidadas. Por lo tanto, tendríamos que tratar de mantener los selectores pero prescindiendo de asociar todos esos métodos que seguramente no precisemos.

Aquí es donde pueden resultar útiles las librerías minimalistas o aquellas especializadas únicamente en seleccionar elementos.

Librerías minimalistas

Existen multitud de librerías a día de hoy con características similares a jQuery que incluso comparten sintaxis. Mootools es quizá la mejor posicionada pero su uso resulta igual de pesado. Busquemos entonces otras opciones.

Zepto.js

De entre las que podemos considerar librerías de tipo minimalista, podemos destacar Zepto.js. Con apenas 5Kbs, se autodefine como una alternativa compatible con jQuery especialmente dirigida a entornos muy exigentes en rendimiento.

Actualmente, este proyecto pretende ofrecer además de los selectores, muchos de los métodos y eventos de jQuery prescindiendo de aquellos de uso menos frecuente. Con una estructura completamente modular, sólo tendremos que cargar los archivos que necesitemos en cada parte concreta de nuestra aplicación: AJAX, eventos, efectos, etc…

De este modo, si tenemos una aplicación en la que necesitamos asociar un evento ‘click’ a una serie de elementos en pantalla, sólo tendríamos que cargar dos dependencias: el núcleo principal y el correspondiente módulo para manejar eventos:

<script type="text/javascript">zepto/zepto.js</script>
<script type="text/javascript">zepto/event.js</script>

Ahora, continuaríamos programando como si estuviésemos trabajando con jQuery:

$('.targetElement').bind('click', function(){
  // Some amazing action...
});

Como podemos comprobar, la sintaxis es idéntica (compatible) y hemos ahorrado tanto en peso de carga como en rendimiento a la hora de asociar métodos al conjunto de elementos seleccionado.

XUI

Podemos considerar a XUI una recién llegada pese a que está en desarrollo desde 2008. Está especialmente enfocada para el desarrollo de aplicaciones móviles con sus apenas 4.1 Kbs y una interesante colección de métodos disponibles.

Su sintaxis también es heredera de jQuery:

// a trivial example
x$('#btn').click( function (e) {
  x$('#msg').html('Thanks for your submission!');
})

En este caso, el álias $ se ha cambiado por x$. No encontraremos más cambios significativos.

Aunque hemos ahorrado muchos recursos, tanto los eventos como el núcleo de Zepto o XIU incluyen más posibilidades que las que realmente podemos necesitar: podemos continuar investigando para reducir aún más los requisitos.

Librerías Selectoras

Pensemos en una aplicación en la que sólo necesitamos la capacidad de utilizar los selectores de tipo CSS y asociar después un determinado evento sobre los mismos a mano; nada más.

Para estos casos, podemos recurrir a las librerías que internamente utilizan otras más complejas como jQuery o MooTools y cuyo rendimiento está en permanente optimización.

De las muchas opciones disponibles para este fin, comentaremos dos de ellas: la famosa Sizzle y la más reciente Qwery.

Sizzle

Creada por John Resig, Sizzle es la librería más utilizada en grandes proyectos y el motor de selección interno de jQuery.

Sus características más destacadas son:

  • Completamente independiente. No requiere de ninguna dependencia.
  • Rendimiento competitivo para los selectores de uso más frecuentes.
  • Sólo 4Kbs.
  • Altamente extensible mediante una sencilla y potente API.
  • Soporte para selectores CSS3.

Como cabe de esperar, su uso es completamente familiar para los desarrolladores jQuery.

Como ejemplo, volvemos a seleccionar las etiquetas anchor de una página que, en esta ocasión, tengan la asignada clase foo:

var anchorCollection = Sizzle( 'a.foo' );
console.log( anchorCollection );

Si echamos de menos el álias $ para iniciar nuestras queries, solo tenemos que hacer una simple asignación antes de comenzar a operar:

var $ = Sizzle;
var anchorCollection = $( 'a.foo' );

Qwery

Desarrollada por Dustin Diaz, Qwery es la última incorporación a las librerías selectoras de sintaxis tipo CSS3.

Su tamaño es el menor de todos a día de hoy con tan sólo 1Kb, lo que representa 1/4 del tamaño de Sizzle.

Basada en la ya legendaria librería getElementBySelector() de Simon Willison que sirvió de inspiración a todas las que vinieron detrás, su sintaxis de nuevo es familiar a los desarrolladores jQuery:

var myCollection = qwery( '#foo a[href=bar]' );

Simple y ligera, Qwery representa una alternativa muy interesante a la hora de seleccionar una librería con el único fin de seleccionar elementos en el DOM.

¿Pero y si necesitamos también asociar eventos a los elementos seleccionados?

Pues en este caso, utilizaremos Javascript plano que es, al fin y al cabo, la forma más limpia y transparente de hacerlo.

Para ello, sólo tenemos que hacer uso de un patrón tipo FACADE que nos garantice su correcto funcionamiento en un entorno multinavegador:

var addMyEvent = function(el,ev,fn){
  if( el.addEventListener ){
    el.addEventListener( ev, fn, false );
  }else if( el.attachEvent ){
   el.attachEvent( 'on'+ev, fn );
  } else {
    el[ 'on' + ev ] = fn;
  }
};

Aunque poco optimizado y muy simple, el ejemplo anterior permitiría asociar un evento a un determinado elemento previamente recogido por alguna de las librerías comentadas:

var myLayer = $('#myLayer');
addMyEvent( myLayer, 'click', function(){
  // Do something awesome here...
})

En este caso, reducimos al máximo las dependencias de nuestro script para su uso en aquellas aplicaciones cuyos requisitos sean básicos y a la vez exigentes en rendimiento.

Conclusión

Librerías como jQuery pueden suponer una herramienta muy importante para construir grandes aplicaciones con poco esfuerzo. Sin embargo, en nuestro día a día, podemos encontrarnos con proyectos que solo requieren una parte muy pequeña de la funcionalidad ofrecida por estos gigantes.

En el actual mercado del software, donde los dispositivos móviles están adquiriendo cada vez una mayor presencia, tenemos que ser conscientes de la importancia de reducir al máximo los costes tanto de tamaño de ficheros como de rendimiento en nuestros scripts. Si realmente solo necesitamos la capacidad de selccionar elementos y asociarles un evento concreto, las librerías más minimalistas o incluso únicamente las selectoras, pueden ser la mejor opción.

Finalmente, será el diseño, los requisitos de cada proyecto y nuestra experiencia las que determinen qué herramientas son las más idóneas para llevarlos a cabo.

Más:

{6} Comentarios.

  1. Jordi Rivero

    Un articulo excelente Carlos.

    Me gustaría puntualizar algo. Un proyecto pequeño, tendrá necesidades pequeñas, fácilmente, muchos recursos para desperdiciar y sobretodo, poco DOM para parsear. Es decir, tendrás pocos problemas sobrecargando con jquery.

    En mi opinión, más bien són los proyectos grandes con grandes exigencias los que te hacen pararte a optimizar quitando lo innecesario o bien los que de inicio tienen pocos recursos que ofrecer, como por ejemplo las plataformas moviles.

    De todas formas, de inicio esta claro que mejor recurrir a lo minimo necesario 🙂

    Al margen, me gusta mucho la filosofía de zepto y me encantaría que jquery la aplicase, además, me gustaría que cambiasen el sizzle por el qwery 😛

    Un saludo!

    • Carlos Benítez

      Completamente de acuerdo en cuanto a que un proyecto pequeño tendrá exigencias pequeñas. Sin embargo, la idea es como bien dices, conocer las opciones más adecuadas para cada caso.

      Yo por ejemplo, siempre incluía jQuery por defecto en mis trabajos, hasta que empecé a encontrarme con problemas en móviles y en videoconsolas (PlayStation 3 se pega con jQuery). Desde entonces, me puse a investigar otras alternativas no solo para aquellos proyectos exigentes sino también para los más modestos. Pero está claro que es en el campo de los móviles donde más se agradece el cambio.

      Y seguramente John Resig le esté echando ya un vistazo a Qwery para optimizar aún más su Sizzle; la siguiente versión vendrá con 10Kbs menos 🙂
      Saludos!

  2. JuniHH

    Varios amigos y mi jefe me dicen que debería probar con jQuery, que ofrece muchas ventajas y evita escribir código extra, que evito los haladera de cabellos cuando un script no funciona con IE6-7, entre otras cosas y aunque tienen razón, la verdad aún no le encuentro la vuelta precisamente por lo mismo que te llevó a escribir este artículo: Rendimiento de carga de jQuery u otros.

    Aún escribo mi propio código y la verdad me siento bastante cómodo porque me garantiza cargas livianas en mis páginas, independientemente a que el servidor las comprima. También me aseguro de usar los minifier’s, porque tiendo a usar muchos comentarios y espacios.

    Ya que varios de los ejemplos que pusiste eran para colección de tags, comparto una de mis funciones para lo mismo. Si no quiero hacer una colección general de todos los elementos sino de casos específicos, aplico una clase a cada objeto aunque sea de forma simbólica y filtro estos con la función (además es liviano):

    var clsFltr = function (id,tg,cl)
    {
    	if ( !document.getElementById(id) ) return;
    	
    	var obAll = document.getElementById(id).getElementsByTagName(tg), obCn = obAll.length;
    	var obs = [], obsNx = 0;
    	
    	for (var x = 0; x < obCn; x++)
    	{
    		if (String(obAll[x].className).indexOf(cl) != -1)
    		{
    			obs[obsNx] = obAll[x];
    			obsNx ++;
    		};
    	};
    	
    	return obs;
    };
    

    Por otro lado, encontré este blog recientemente desde un link en Twitter y ya forma parte de mis lecturas favoritas. El sábado pasado duré toda la mañana deborando artículos viejos. Muchas felicidades Carlos, haces un gran trabajo acá.

    • Carlos Benítez

      Hola!
      Es cierto que las librerías suponen una pequeña penalización a la hora de cargar una página pero también lo es que suelen ofrecer soporte multinavegador para todos aquellos métodos que implementan. Eso es un tema muy interesante que nos puede ahorrar muchos quebraderos de cabeza como bien comentas. Al final, siempre tememos que sopesar los pros y contras dependiendo de la funcionalidad que queramos incluir en un proyecto.

      Sin embargo, otra cosa que tenemos que medir es el rendimiento de aquellas funciones que nosotros implementemos a mano frente a las que ofrecen estas librerías. La función que has compartido puede, por ejemplo, hacerse un poco más rápida cambiando el modo en que se recorre el array de elementos encontrados, evitando la declaración de algunas variables intermedias que no son necesarias y omitiendo el CAST:

      var clsFltr = function (id, tg, cl){
      	if ( !document.getElementById( id ) ) return;
      
      	var obAll = document.getElementById( id ).getElementsByTagName( tg ), obCn = obAll.length;
      	var obs = [];
      
      	while(obCn--){
      		if (obAll[ obCn ].className.indexOf( cl ) != -1){
      			obs.push( obAll[ obCn ] );
      		}
      	}
      
      	return obs;
      };
      

      Utilizando un ‘reverse loop‘ que para este caso funciona bien, evitamos declarar ‘x’ e incrementamos notablemente el rendimiento para conjuntos grandes.
      El método ‘push’ también evita al intérprete el cálculo de la asignación directa del índice en el array ‘obs’.
      Finalmente, eliminando el CAST String, evitamos la comprobación de tipos interna.

      Y con eso, pues tenemos nuestras necesidades, tanto de funcionalidad como de rendimiento, cubiertas 🙂
      Gracias a ti por leer este blog y por compartir con nosotros tu comentario.

      Un saludo!

  3. Diego

    El último ejemplo:

    var myLayer = $(‘#myLayer’);
    addEventListener( myLayer, ‘click’, function(){
    // Do something awesome here…
    })

    ¿no debería ser?:

    var myLayer = $(‘#myLayer’);
    addMyEvent( myLayer, ‘click’, function(){
    // Do something awesome here…
    })

    En caso contrario, pido disculpas 🙂

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *