Cómo mejorar el rendimiento de su aplicación HTML5

Introducción

HTML5 nos brinda excelentes herramientas para mejorar el aspecto visual de las aplicaciones web. Esto es especialmente cierto en el ámbito de las animaciones. Sin embargo, con este nuevo poder también vienen nuevos desafíos. En realidad, estos desafíos no son tan nuevos y, a veces, puede ser conveniente preguntarle a tu vecino de escritorio, el programador de Flash, cómo superó situaciones similares en el pasado.

De cualquier manera, cuando trabajas en animación, es muy importante que los usuarios perciban que estas animaciones son fluidas. Lo que debemos tener en cuenta es que la fluidez de las animaciones no se puede crear simplemente aumentando los fotogramas por segundo más allá de cualquier umbral cognitivo. Lamentablemente, nuestro cerebro es más inteligente que eso. Lo que aprenderás es que 30 fotogramas de animación por segundo (fps) reales son mucho mejores que 60 fps con solo unos pocos fotogramas en el medio. A las personas no les gusta la irregularidad.

En este artículo, intentaremos brindarte las herramientas y técnicas para que trabajes en la mejora de la experiencia de tu propia aplicación.

La estrategia

De ninguna manera queremos desalentarte de crear apps visuales increíbles con HTML5.

Luego, cuando notes que el rendimiento podría ser un poco mejor, vuelve a esta página y lee sobre cómo puedes mejorar los elementos de tu aplicación. Por supuesto, puede ayudar a hacer algunas cosas bien en primer lugar, pero nunca dejes que eso se interponga en tu camino para ser productivo.

Fidelidad visual++ con HTML5

Aceleración de hardware

La aceleración de hardware es un logro importante para el rendimiento general de la renderización en el navegador. El esquema general consiste en transferir las tareas que, de otro modo, calcularía la CPU principal a la unidad de procesamiento de gráficos (GPU) en el adaptador de gráficos de la computadora. Esto puede generar grandes mejoras de rendimiento y también reducir el consumo de recursos en dispositivos móviles.

La GPU puede acelerar estos aspectos de tu documento

  • Compilación general del diseño
  • Transiciones de CSS3
  • Transformaciones en 3D de CSS3
  • Dibujo en lienzo
  • Dibujo en 3D de WebGL

Si bien la aceleración de Canvas y WebGL son funciones de propósito especial que podrían no aplicarse a tu aplicación específica, los tres primeros aspectos pueden ayudar a que casi todas las apps sean más rápidas.

¿Qué se puede acelerar?

La aceleración de GPU funciona transfiriendo tareas específicas y bien definidas al hardware de propósito especial. El esquema general es que tu documento se divide en varias "capas" que son invariables a los aspectos de tu página que se aceleran. Estas capas se renderizan con la canalización de renderización tradicional. Luego, se usa la GPU para combinar las capas en una sola página aplicando los "efectos" que se pueden acelerar sobre la marcha. Un resultado posible es que un objeto animado en la pantalla no requiera un solo "reajuste" de la página mientras se produce la animación.

Lo que debes tener en cuenta es que debes facilitar que el motor de renderización identifique cuándo puede aplicar su magia de aceleración de GPU. Consulta el siguiente ejemplo:

Si bien esto funciona, el navegador no sabe realmente que estás realizando algo que un ser humano debería percibir como una animación fluida. Considera lo que sucede cuando logras la misma apariencia visual con transiciones CSS3:

La forma en que el navegador implementa esta animación está completamente oculta para el desarrollador. Esto, a su vez, significa que el navegador puede aplicar trucos, como la aceleración de la GPU, para lograr el objetivo definido.

Existen dos marcas de línea de comandos útiles para Chrome que ayudan a depurar la aceleración de la GPU:

  1. --show-composited-layer-borders muestra un borde rojo alrededor de los elementos que se manipulan a nivel de la GPU. Es útil para confirmar que tus manipulaciones se producen dentro de la capa de la GPU.
  2. --show-paint-rects Se pintan todos los cambios que no son de la GPU, lo que arroja un borde claro alrededor de todas las áreas que se vuelven a pintar. Puedes ver el navegador optimizando las áreas de pintura en acción.

Safari tiene marcas de tiempo de ejecución similares descritas aquí.

Transiciones de CSS3

Las transiciones de CSS hacen que la animación de diseño sea trivial para todos, pero también son una función de rendimiento inteligente. Debido a que el navegador administra una transición de CSS, la fidelidad de su animación puede mejorarse mucho y, en muchos casos, acelerarse el hardware. Actualmente, WebKit (Chrome, Safari, iOS) tiene transformaciones de CSS aceleradas por hardware, pero pronto estará disponible en otros navegadores y plataformas.

Puedes usar los eventos transitionEnd para crear secuencias de comandos con combinaciones potentes, aunque, en este momento, capturar todos los eventos de fin de transición admitidos significa observar webkitTransitionEnd transitionend oTransitionEnd.

Muchas bibliotecas ahora presentan APIs de animación que aprovechan las transiciones si están presentes y recurren a la animación estándar de estilo DOM de lo contrario. scripty2, YUI transition, jQuery animate enhanced.

CSS3 Translate

Seguro que ya animaste la posición x/y de un elemento en la página. Es probable que hayas manipulado las propiedades izquierda y superior del estilo intercalado. Con las transformaciones 2D, podemos usar la funcionalidad translate() para replicar este comportamiento.

Podemos combinar esto con la animación del DOM para usar la mejor opción posible.

<div style="position:relative; height:120px;" class="hwaccel">

  <div style="padding:5px; width:100px; height:100px; background:papayaWhip;
              position:absolute;" id="box">
  </div>
</div>

<script>
document.querySelector('#box').addEventListener('click', moveIt, false);

function moveIt(evt) {
  var elem = evt.target;

  if (Modernizr.csstransforms && Modernizr.csstransitions) {
    // vendor prefixes omitted here for brevity
    elem.style.transition = 'all 3s ease-out';
    elem.style.transform = 'translateX(600px)';

  } else {
    // if an older browser, fall back to jQuery animate
    jQuery(elem).animate({ 'left': '600px'}, 3000);
  }
}
</script>

Usamos Modernizr para probar las funciones de las transformaciones 2D y las transiciones de CSS. Si es así, usaremos translate para cambiar la posición. Si se anima con una transición, es probable que el navegador pueda acelerarlo con hardware. Para darle al navegador otro empujón en la dirección correcta, usaremos la “bala de CSS mágica” de arriba.

Si nuestro navegador tiene menos capacidades, recurriremos a jQuery para mover nuestro elemento. Puedes usar el complemento de polyfill de jQuery Transform de Louis-Remi Babe para que todo esto sea automático.

window.requestAnimationFrame

Mozilla presentó requestAnimationFrame y WebKit lo iteró con el objetivo de proporcionarte una API nativa para ejecutar animaciones, ya sea que se basen en DOM o CSS, o en <canvas> o WebGL. El navegador puede optimizar las animaciones simultáneas en un solo ciclo de reflujo y repintado, lo que genera una animación de mayor fidelidad. Por ejemplo, animaciones basadas en JS sincronizadas con transiciones de CSS o SMIL de SVG. Además, si ejecutas el bucle de animación en una pestaña que no es visible, el navegador no lo mantendrá en ejecución, lo que significa menos uso de CPU, GPU y memoria, lo que genera una mayor duración de batería.

Para obtener más detalles sobre cómo y por qué usar requestAnimationFrame, consulta el artículo de Paul Irish requestAnimationFrame para animaciones inteligentes.

Generación de perfiles

Cuando descubras que se puede mejorar la velocidad de tu aplicación, es hora de analizar la generación de perfiles para saber dónde las optimizaciones podrían generar el mayor beneficio. A menudo, las optimizaciones tendrán un impacto negativo en la capacidad de mantenimiento de tu código fuente, por lo que solo deben aplicarse si es necesario. El perfilado te indica qué partes de tu código tendrían los mayores beneficios si se mejorara su rendimiento.

Generador de perfiles de JavaScript

Los generadores de perfiles de JavaScript te proporcionan una descripción general del rendimiento de tu aplicación a nivel de la función de JavaScript midiendo el tiempo que tarda en ejecutarse cada función individual desde su inicio hasta su finalización.

El tiempo de ejecución bruto de una función es el tiempo total que se tarda en ejecutarla de arriba abajo. El tiempo de ejecución neto es el tiempo de ejecución bruto menos el tiempo que se tardó en ejecutar las funciones a las que se llamó desde la función.

A algunas funciones se las llama con más frecuencia que a otras. Los generadores de perfiles suelen proporcionarte el tiempo que tardaron en ejecutarse todas las invocaciones, así como el tiempo de ejecución promedio, mínimo y máximo.

Para obtener más detalles, consulta la documentación de Chrome DevTools sobre la generación de perfiles.

El DOM

El rendimiento de JavaScript tiene una gran influencia en la fluidez y la capacidad de respuesta de tu aplicación. Es importante comprender que, si bien los generadores de perfiles de JavaScript miden el tiempo de ejecución de tu código, también miden indirectamente el tiempo dedicado a realizar operaciones del DOM. Estas operaciones del DOM suelen ser el origen de los problemas de rendimiento.

function drawArray(array) {
  for(var i = 0; i < array.length; i++) {
    document.getElementById('test').innerHTML += array[i]; // No good :(
  }
}

Por ejemplo, en el código anterior, casi no se dedica tiempo a ejecutar JavaScript real. Es muy probable que la función drawArray aparezca en tus perfiles porque interactúa con el DOM de una manera muy ineficiente.

Sugerencias y trucos

Funciones anónimas

Las funciones anónimas no son fáciles de perfilar porque, de forma inherente, no tienen un nombre con el que puedan aparecer en el generador de perfiles. Existen dos maneras de solucionar este problema:

$('.stuff').each(function() { ... });

Vuelve a escribir en:

$('.stuff').each(function workOnStuff() { ... });

No es muy conocido que JavaScript admite nombres de expresiones de función. De esta manera, se mostrarán perfectamente en el generador de perfiles. Hay un problema con esta solución: la expresión con nombre, en realidad, coloca el nombre de la función en el alcance léxico actual. Esto podría anular otros símbolos, así que ten cuidado.

Genera perfiles de funciones largas

Imagina que tienes una función larga y sospechas que una pequeña parte de ella podría ser la causa de tus problemas de rendimiento. Existen dos maneras de averiguar qué parte es el problema:

  1. El método correcto: Refactoriza tu código para que no incluya funciones largas.
  2. El método malvado para hacer las cosas: agrega instrucciones en forma de funciones de llamada a sí mismas con nombre a tu código. Si tienes un poco de cuidado, esto no cambiará la semántica y hará que partes de tu función aparezcan como funciones individuales en el generador de perfiles: js function myLongFunction() { ... (function doAPartOfTheWork() { ... })(); ... } No olvides quitar estas funciones adicionales después de que se complete la generación de perfiles o incluso usarlas como punto de partida para refactorizar tu código.

Perfilado de DOM

Las herramientas de desarrollo más recientes de Chrome Web Inspector contienen la nueva "Vista de cronograma", que muestra un cronograma de las acciones de bajo nivel que realiza el navegador. Puedes usar esta información para optimizar tus operaciones de DOM. Debes reducir la cantidad de "acciones" que el navegador debe realizar mientras se ejecuta tu código.

La vista de cronograma puede crear una gran cantidad de información. Por lo tanto, debes intentar crear casos de prueba mínimos que puedas ejecutar de forma independiente.

Perfilado de DOM

En la imagen anterior, se muestra el resultado de la vista de cronograma de una secuencia de comandos muy simple. El panel izquierdo muestra las operaciones que realiza el navegador en orden cronológico, mientras que el cronograma del panel derecho muestra el tiempo real que consume una operación individual.

Más información sobre la vista de cronograma. Una herramienta alternativa para generar perfiles en Internet Explorer es DynaTrace Ajax Edition.

Estrategias de perfilado

Destaca aspectos

Cuando quieras generar perfiles de tu aplicación, intenta identificar los aspectos de su funcionalidad que podrían provocar lentitud lo más cerca posible. Luego, intenta realizar una ejecución de perfil que solo ejecute las partes del código que sean relevantes para estos aspectos de la aplicación. Esto facilitará la interpretación de los datos de generación de perfiles, ya que no se mezclarán con instrucciones de código que no estén relacionadas con tu problema real. Estos son algunos buenos ejemplos de aspectos individuales de tu solicitud:

  1. Tiempo de inicio (activa el generador de perfiles, vuelve a cargar la aplicación, espera a que se complete la inicialización y detén el generador de perfiles.
  2. Haz clic en un botón y en la animación posterior (inicia el generador de perfiles, haz clic en el botón, espera a que se complete la animación y detén el generador de perfiles).
Generador de perfiles de GUI

Ejecutar solo la parte correcta de la aplicación puede ser más difícil en un programa de GUI que cuando optimizas, por ejemplo, el trazador de rayos de tu motor 3D. Por ejemplo, si deseas generar perfiles de lo que sucede cuando haces clic en un botón, es posible que actives eventos de desplazamiento del mouse no relacionados en el camino que hagan que tus resultados sean menos concluyentes. Intenta evitarlo. :)

Interfaz programática

También hay una interfaz programática para activar el depurador. Esto permite un control preciso sobre cuándo comienza y termina la generación de perfiles.

Inicia una generación de perfiles con lo siguiente:

console.profile()

Detén la generación de perfiles con lo siguiente:

console.profileEnd()

Repetibilidad

Cuando crees perfiles, asegúrate de poder reproducir los resultados. Solo entonces podrás saber si tus optimizaciones realmente mejoraron algo. Además, la generación de perfiles a nivel de la función se realiza en el contexto de toda la computadora. No es una ciencia exacta. Las ejecuciones de perfiles individuales pueden verse afectadas por muchos otros factores que ocurren en tu computadora:

  1. Un temporizador no relacionado en tu propia aplicación que se activa mientras mides otra cosa
  2. El recolector de elementos no utilizados realiza su trabajo.
  3. Otra pestaña en tu navegador que realiza un trabajo intensivo en el mismo subproceso operativo
  4. Otro programa de la computadora que consume la CPU, lo que ralentiza la aplicación.
  5. Cambios repentinos en el campo gravitacional de la Tierra.

También tiene sentido ejecutar la misma ruta de código varias veces en una sesión de generación de perfiles. De esta manera, disminuyes la influencia de los factores anteriores y las partes lentas pueden destacarse aún más.

Mide, mejora y vuelve a medir

Cuando identifiques un punto lento en tu programa, intenta pensar en formas de mejorar el comportamiento de ejecución. Después de cambiar el código, vuelve a perfilar. Si estás conforme con el resultado, continúa. Si no ves una mejora, probablemente deberías revertir el cambio y no dejarlo "porque no puede hacer daño".

Estrategias de optimización

Minimiza la interacción con el DOM

Un tema común para mejorar la velocidad de las aplicaciones cliente web es minimizar la interacción con el DOM. Si bien la velocidad de los motores de JavaScript aumentó en un orden de magnitud, el acceso al DOM no se aceleró al mismo ritmo. Esto también es por razones muy prácticas que nunca sucederán (las tareas como el diseño y el dibujo en una pantalla llevan tiempo).

Almacena en caché los nodos DOM

Cada vez que recuperes un nodo o una lista de nodos del DOM, intenta pensar si podrías volver a usarlos en un cálculo posterior (o incluso en la siguiente iteración del bucle). Esto suele suceder, siempre y cuando no agregues ni quites nodos en el área relevante.

Antes:

function getElements() {
  return $('.my-class');
}

Después:

var cachedElements;
function getElements() {
  if (cachedElements) {
    return cachedElements;
  }
  cachedElements = $('.my-class');
  return cachedElements;
}

Almacena en caché los valores de los atributos

De la misma manera que puedes almacenar en caché los nodos DOM, también puedes almacenar en caché los valores de los atributos. Imagina que estás animando un atributo del estilo de un nodo. Si sabes que tú (como en esa parte del código) eres el único que tocará ese atributo, puedes almacenar en caché el último valor en cada iteración para no tener que leerlo de forma reiterada.

Antes:

setInterval(function() {
  var ele = $('#element');
  var left = parseInt(ele.css('left'), 10);
  ele.css('left', (left + 5) + 'px');
}, 1000 / 30);

Después: js var ele = $('#element'); var left = parseInt(ele.css('left'), 10); setInterval(function() { left += 5; ele.css('left', left + 'px'); }, 1000 / 30);

Quita la manipulación del DOM de los bucles

Los bucles suelen ser puntos críticos para la optimización. Intenta pensar en formas de desacoplar el procesamiento de números real para trabajar con el DOM. A menudo, es posible hacer un cálculo y, luego de hacerlo, aplicar todos los resultados de una sola vez.

Antes:

document.getElementById('target').innerHTML = '';
for(var i = 0; i < array.length; i++) {
  var val = doSomething(array[i]);
  document.getElementById('target').innerHTML += val;
}

Después:

var stringBuilder = [];
for(var i = 0; i < array.length; i++) {
  var val = doSomething(array[i]);
  stringBuilder.push(val);
}
document.getElementById('target').innerHTML = stringBuilder.join('');

Volver a dibujar y volver a fluir

Como se mencionó antes, el acceso al DOM es relativamente lento. Se vuelve muy lento cuando tu código lee un valor que se debe volver a calcular porque recientemente modificó algo relacionado en el DOM. Por lo tanto, se debe evitar mezclar el acceso de lectura y escritura al DOM. Lo ideal es que tu código siempre se agrupe en dos fases:

  • Fase 1: Lee los valores del DOM necesarios para tu código
  • Fase 2: Modifica el DOM

Intenta no programar un patrón como el siguiente:

  • Fase 1: Lee los valores del DOM
  • Fase 2: Modifica el DOM
  • Fase 3: Lee más
  • Fase 4: Modifica el DOM en otro lugar.

Antes:

function paintSlow() {
  var left1 = $('#thing1').css('left');
  $('#otherThing1').css('left', left);
  var left2 = $('#thing2').css('left');
  $('#otherThing2').css('left', left);
}

Después:

function paintFast() {
  var left1 = $('#thing1').css('left');
  var left2 = $('#thing2').css('left');
  $('#otherThing1').css('left', left);
  $('#otherThing2').css('left', left);
}

Este consejo debe considerarse para las acciones que se producen dentro de un contexto de ejecución de JavaScript. (p.ej., dentro de un controlador de eventos, dentro de un controlador de intervalo o cuando se controla una respuesta de Ajax).

Si ejecutas la función paintSlow() anterior, se crea esta imagen:

paintSlow()

Si cambias a la implementación más rápida, se obtiene esta imagen:

Implementación más rápida

Estas imágenes muestran que reordenar la forma en que tu código accede al DOM puede mejorar en gran medida el rendimiento de la renderización. En este caso, el código original debe volver a calcular los estilos y diseñar la página dos veces para crear el mismo resultado. Se puede aplicar una optimización similar a prácticamente todo el código del "mundo real" y obtener resultados realmente espectaculares.

Más información: Renderización: repintado, reabastecimiento/reacondicionamiento, cambio de diseño de Stoyan Stefanov

Redibujos y el bucle de eventos

La ejecución de JavaScript en el navegador sigue un modelo de "bucle de eventos". De forma predeterminada, el navegador está en estado "inactivo". Este estado puede interrumpirse por eventos de interacciones del usuario o por elementos como temporizadores de JavaScript o devoluciones de llamada de Ajax. Cada vez que se ejecuta un fragmento de JavaScript en un punto de interrupción, el navegador suele esperar a que finalice hasta que vuelva a pintar la pantalla (puede haber excepciones para JavaScripts de ejecución extremadamente larga o en casos como cuadros de alerta que interrumpen de manera efectiva la ejecución de JavaScript).

Consecuencias

  1. Si los ciclos de animación de JavaScript tardan más de 1/30 de segundo en ejecutarse, no podrás crear animaciones fluidas porque el navegador no volverá a pintar durante la ejecución de JS. Cuando esperas controlar también los eventos del usuario, debes ser mucho más rápido.
  2. A veces, resulta útil retrasar algunas acciones de JavaScript hasta un poco más tarde. p.ej., setTimeout(function() { ... }, 0) Esto le indica al navegador que ejecute la devolución de llamada en cuanto el bucle de eventos vuelva a estar inactivo (algunos navegadores esperarán al menos 10 ms). Ten en cuenta que esto creará dos ciclos de ejecución de JavaScript que estarán muy cerca en el tiempo. Ambos pueden activar un nuevo pintado de la pantalla, lo que podría duplicar el tiempo total dedicado a la pintura. Si esto realmente activa dos pinturas depende de las heurísticas del navegador.

Versión normal:

function paintFast() {
  var height1 = $('#thing1').css('height');
  var height2 = $('#thing2').css('height');
  $('#otherThing1').css('height', '20px');
  $('#otherThing2').css('height', '20px');
}
Redibujos y el bucle de eventos

Agreguemos un retraso:

function paintALittleLater() {
  var height1 = $('#thing1').css('height');
  var height2 = $('#thing2').css('height');
  $('#otherThing1').css('height', '20px');
  setTimeout(function() {
    $('#otherThing2').css('height', '20px');
  }, 10)
}
Demora

La versión retrasada muestra que el navegador pinta dos veces, aunque los dos cambios en la página son solo de 1/100 de segundo cada uno.

Inicialización diferida

Los usuarios quieren que las apps web se carguen rápido y sean responsivas. Sin embargo, los usuarios tienen diferentes umbrales de lo que perciben como lento según la acción que realizan. Por ejemplo, una app nunca debe realizar muchas operaciones de procesamiento en un evento de desplazamiento del mouse porque esto podría crear una mala experiencia del usuario mientras este sigue moviendo el mouse. Sin embargo, los usuarios están acostumbrados a aceptar una pequeña demora después de hacer clic en un botón.

Por lo tanto, podría tener sentido mover el código de inicialización para que se ejecute lo más tarde posible (p.ej., cuando el usuario hace clic en un botón que activa un componente en particular de tu aplicación).

Antes: js var things = $('.ele > .other * div.className'); $('#button').click(function() { things.show() });

Después: js $('#button').click(function() { $('.ele > .other * div.className').show() });

Delegación de eventos

Distribuir controladores de eventos en una página puede llevar un tiempo relativamente largo y también puede ser tedioso una vez que los elementos se reemplazan de forma dinámica, lo que requiere volver a adjuntar los controladores de eventos a los elementos nuevos.

La solución en este caso es usar una técnica llamada delegación de eventos. En lugar de adjuntar controladores de eventos individuales a los elementos, se usa la naturaleza de burbujeo de muchos eventos del navegador adjuntando el controlador de eventos a un nodo superior y verificando el nodo de destino del evento para ver si es de interés.

En jQuery, esto se puede expresar fácilmente de la siguiente manera:

$('#parentNode').delegate('.button', 'click', function() { ... });

Cuándo no usar la delegación de eventos

A veces, puede ocurrir lo contrario: usas la delegación de eventos y tienes un problema de rendimiento. Básicamente, la delegación de eventos permite un tiempo de inicialización de complejidad constante. Sin embargo, el precio de verificar si un evento es de interés se debe pagar por cada invocación de ese evento. Esto puede ser costoso, especialmente para los eventos que ocurren con frecuencia, como "mouse over" o incluso "mouse move".

Problemas y soluciones habituales

Las tareas que realizo en $(document).ready llevan mucho tiempo

Consejo personal de Malte: Nunca hagas nada en $(document).ready. Intenta entregar el documento en su forma final. De acuerdo, puedes registrar objetos de escucha de eventos, pero solo con el selector de ID o la delegación de eventos. En el caso de los eventos costosos, como "mousemove", retrasa el registro hasta que se necesite (evento de desplazamiento del mouse sobre el elemento relevante).

Y si realmente necesitas hacer algo, como realizar una solicitud Ajax para obtener datos reales, muestra una buena animación. Te recomendamos que incluyas la animación como un URI de datos si se trata de un GIF animado o algo similar.

Desde que agregué una película Flash a la página, todo funciona muy lento

Agregar Flash a una página siempre ralentizará un poco la renderización porque el diseño final de la ventana debe “negociarse” entre el navegador y el complemento Flash. Cuando no puedas evitar colocar Flash en tus páginas, asegúrate de establecer el parámetro de Flash "wmode" en el valor "window" (que es el predeterminado). Esto inhabilitará la capacidad de combinar elementos HTML y Flash (no podrás ver un elemento HTML que se encuentre sobre la película Flash, y esta no podrá ser transparente). Esto puede ser un inconveniente, pero mejorará significativamente tu rendimiento. Por ejemplo, observa la forma en que youtube.com evita colocar capas sobre el reproductor de películas principal.

Guardo elementos en localStorage, ahora mi aplicación tartamudea

La escritura en localStorage es una operación síncrona que implica activar el disco duro. Nunca debes realizar operaciones sincrónicas de "larga duración" mientras realizas animaciones. Mueve el acceso a localStorage a un lugar de tu código en el que tengas la certeza de que el usuario está inactivo y no se están ejecutando animaciones.

El perfil indica que un selector de jQuery es muy lento.

Primero, debes asegurarte de que tu selector se pueda ejecutar a través de document.querySelectorAll. Puedes probarlo en la consola de JavaScript. Si hay una excepción, vuelve a escribir el selector para que no use ninguna extensión especial de tu framework de JavaScript. Esto acelerará tu selector en los navegadores modernos en un orden de magnitud.

Si esto no funciona o si también quieres tener velocidad en navegadores modernos, sigue estos lineamientos:

  • En el lado derecho del selector, intenta ser lo más específico posible.
  • Usa un nombre de etiqueta que no uses con frecuencia como la parte del selector más a la derecha.
  • Si nada funciona, considera reescribir el código para poder usar un selector de ID.

Todas estas manipulaciones del DOM llevan mucho tiempo.

Una gran cantidad de inserciones, eliminaciones y actualizaciones de nodos del DOM puede ser muy lenta. Por lo general, se puede optimizar generando una gran cadena de HTML y usando domNode.innerHTML = newHTML para reemplazar el contenido anterior. Ten en cuenta que esto puede ser muy malo para la capacidad de mantenimiento y puede crear vínculos de memoria en IE, así que ten cuidado.

Otro problema común es que tu código de inicialización podría crear mucho HTML. p.ej., un complemento de jQuery que transforma un cuadro de selección en un montón de divs porque eso es lo que querían las personas de diseño sin tener en cuenta las prácticas recomendadas de UX. Si realmente quieres que tu página sea rápida, nunca lo hagas. En su lugar, envía todo el marcado del servidor en su forma final. Esto también tiene muchos problemas, así que piensa bien si la velocidad vale la pena.

Herramientas

  1. JSPerf: Realiza comparativas de pequeños fragmentos de JavaScript
  2. Firebug: Para generar perfiles en Firefox
  3. Herramientas para desarrolladores de Google Chrome (disponible como WebInspector en Safari)
  4. DOM Monster: Para optimizar el rendimiento del DOM
  5. DynaTrace Ajax Edition: Para generar perfiles y realizar optimizaciones de pintura en Internet Explorer

Lecturas adicionales

  1. Velocidad de Google
  2. Paul Irish sobre el rendimiento de jQuery
  3. Rendimiento extremo de JavaScript (presentación de diapositivas)