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

Introducción

HTML5 nos brinda excelentes herramientas para mejorar la apariencia visual de las aplicaciones web. Esto es más evidente en el ámbito de las animaciones. Sin embargo, con este nuevo poder, también surgen nuevos desafíos. En realidad, estos desafíos no son realmente tan nuevos y, a veces, tiene sentido preguntarle a tu compañero vecino de escritorio, el programador de Flash, cómo ha superado cosas similares en el pasado.

De todos modos, cuando trabajas en animaciones, se vuelve muy importante que los usuarios perciban que estas animaciones son fluidas. Debemos darnos cuenta de 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 por segundo (FPS) reales es mucho mejor que 60 FPS con solo unos pocos fotogramas en el medio. Las personas odian la dentadura.

En este artículo, trataremos de ofrecerte las herramientas y técnicas necesarias para trabajar en mejorar la experiencia de tu propia aplicación.

La estrategia

De ninguna manera queremos disuadirte de que crees aplicaciones asombrosas y visualmente atractivas con HTML5.

Luego, cuando notes que el rendimiento podría ser un poco mejor, regresa aquí y lee sobre cómo puedes mejorar los elementos de tu aplicación. Por supuesto, puede ayudarte hacer algunas cosas bien al principio, pero nunca dejar que eso te impida ser productivo.

Fidelidad visual++ con HTML5

Aceleración de hardware

La aceleración de hardware es un hito importante para el rendimiento general de la representación en el navegador. El esquema general consiste en transferir las tareas que, de otro modo, sería calculada por la CPU principal a la unidad de procesamiento gráfico (GPU) del adaptador de gráficos de tu computadora. Esto puede generar grandes aumentos en el rendimiento y reducir el consumo de recursos en dispositivos móviles.

La GPU puede acelerar estos aspectos de tu documento

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

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

¿Qué se puede acelerar?

La aceleración de GPU funciona descargando tareas bien definidas y específicas en hardware de propósito especial. El esquema general consiste en que el documento se divide en varias "capas" que son invariables con respecto a los aspectos de la página que están acelerados. Estas capas se renderizan con la canalización de renderización tradicional. Luego, se usa la GPU para componer 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 "rediseño" de la página mientras se produce la animación.

Lo que debes recordar 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 una acción que se supone que un ser humano percibe 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.

Hay 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 las manipulaciones se producen dentro de la capa de la GPU.
  2. --show-paint-rects, todos los cambios que no son de GPU se pintan, y esto arroja un borde claro alrededor de todas las áreas que se vuelven a pintar. Puedes ver cómo el navegador optimiza las áreas de pintura en acción.

Safari tiene marcas similares de tiempo de ejecución que se describen aquí.

Transiciones CSS3

Las transiciones CSS hacen que la animación de estilo 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 se puede mejorar mucho y, en muchos casos, se puede acelerar por hardware. En la actualidad, WebKit (Chrome, Safari, iOS) tiene transformaciones de CSS aceleradas por hardware, pero llegará rápidamente a otros navegadores y plataformas.

Puedes usar eventos transitionEnd para crear secuencias de comandos de esto en combinaciones potentes, aunque, por el momento, capturar todos los eventos de finalización de transición admitidos significa mirar webkitTransitionEnd transitionend oTransitionEnd.

Muchas bibliotecas ahora introdujeron APIs de Animation que aprovechan las transiciones si están presentes y, de lo contrario, recurren a animaciones de estilo DOM estándar. scripty2, transición de YUI y jQuery animate mejorada.

Traslación de CSS3

Estoy seguro de que ya te has dado cuenta de que estás animando la posición x/y de un elemento en toda la página anteriormente. 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 DOM para usar lo mejor 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 funciones en las transformaciones CSS 2D y en las transiciones de CSS. De ser así, usaremos Translate para cambiar la posición. Si se anima mediante una transición, es muy probable que el navegador pueda acelerarlo. Para darle al navegador otro impulso en la dirección correcta, usaremos la “viñeta mágica de CSS” que se describió anteriormente.

Si nuestro navegador tiene esa capacidad, recurriremos a jQuery para mover el elemento. Puedes usar el complemento de polyfills jQuery Transform de Louis-Remi Babe para que todo se realice de forma automática.

window.requestAnimationFrame

Mozilla introdujo requestAnimationFrame y WebKit lo iteró con el objetivo de proporcionar una API nativa para ejecutar animaciones, ya sea basadas en DOM/CSS, o en <canvas> o WebGL. El navegador puede optimizar animaciones simultáneas en un solo ciclo de reprocesamiento y volver a pintar, lo que lleva a una animación de mayor fidelidad. Por ejemplo, animaciones basadas en JS sincronizadas con transiciones CSS o SMIL en SVG. Además, si estás ejecutando el bucle de animación en una pestaña que no está visible, el navegador no lo hará funcionar, lo que implica un menor uso de CPU, GPU y memoria, lo que aumenta la duración de la batería.

Para obtener más información sobre cómo y por qué usar requestAnimationFrame, consulta el artículo de Paul Irlandés requestAnimationFrame sobre la animación inteligente.

Cómo generar perfiles

Cuando descubras que se puede mejorar la velocidad de tu aplicación, será hora de profundizar en la generación de perfiles para descubrir 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 y, por lo tanto, solo deben aplicarse si es necesario. La generación de perfiles te indica qué partes de tu código producirían los mayores beneficios si se mejorara el rendimiento.

Generación de perfiles de JavaScript

Los generadores de perfiles de JavaScript ofrecen una descripción general del rendimiento de tu aplicación a nivel de función de JavaScript al medir el tiempo que lleva ejecutar cada función individual desde el comienzo hasta el final.

El tiempo total de ejecución 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 bruto de ejecución menos el tiempo que se tardó en ejecutar funciones que se llamaron desde la función.

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

Si deseas obtener más detalles, consulta los documentos de las Herramientas para desarrolladores de Chrome sobre la generación de perfiles.

El DOM

El rendimiento de JavaScript influye mucho en la fluidez y receptividad de la aplicación. Es importante comprender que, si bien los generadores de perfiles de JavaScript miden el tiempo de ejecución de tu JavaScript, también miden indirectamente el tiempo dedicado a realizar operaciones del DOM. A menudo, estas operaciones del DOM son la base de los problemas de rendimiento.

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

P.ej., en el código anterior, casi no se invierte tiempo en ejecutar el código JavaScript real. Es muy probable que la función drawArray-aparezca en tus perfiles porque interactúa con el DOM de forma muy desperdiciada.

Sugerencias y trucos

Funciones anónimas

No es fácil generar perfiles de las funciones anónimas porque no tienen un nombre con el que podrían mostrarse en el generador de perfiles. Existen dos maneras de solucionar esto:

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

reescribir en:

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

No es comúnmente conocido que JavaScript admita la asignación de nombres a expresiones de funciones. 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. Ten cuidado, ya que eso podría hacer que otros símbolos dieran como resultado errores.

Cómo generar perfiles de funciones largas

Imagina que tienes una función larga y sospechas que una pequeña parte de ella podría ser el motivo 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 no incluir funciones largas.
  2. El método malintencionado para realizar tareas: agrega sentencias en forma de funciones de autollamada con nombre a tu código. Si tienes un poco de cuidado, esto no cambiará la semántica y hará que algunas partes de la función se muestren 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.

Generación de perfiles de DOM

Las herramientas de desarrollo más recientes del Inspector web de Chrome contienen la nueva "vista de cronograma", que muestra una línea de tiempo de las acciones de bajo nivel que realiza el navegador. Puedes usar esta información para optimizar tus operaciones de DOM. Debes intentar reducir la cantidad de “acciones” que el navegador debe realizar mientras se ejecuta tu código.

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

Generación de perfiles de DOM

En la imagen anterior, se muestra el resultado de la vista de línea de tiempo para una secuencia de comandos muy simple. En el panel izquierdo, se muestran las operaciones que realizó el navegador en orden crónico, mientras que en el cronograma del panel derecho se muestra el tiempo real consumido por una operación individual.

Obtén más información sobre la vista de línea de tiempo. Una herramienta alternativa para generar perfiles en Internet Explorer es DynaTrace Ajax Edition.

Estrategias de generación de perfiles

Destaca aspectos

Cuando quieras generar un perfil para tu aplicación, intenta destacar los aspectos de su funcionalidad que podrían activar la lentitud lo más cerca posible. Luego, intenta ejecutar un perfil que solo ejecute partes de tu código que sean relevantes para estos aspectos de tu aplicación. Esto hará que los datos de generación de perfiles sean más fáciles de interpretar, ya que no se mezclan con las rutas de código que no están relacionadas con tu problema real. Estos son algunos buenos ejemplos de aspectos individuales de tu aplicación:

  1. Hora de inicio (activa el generador de perfiles, vuelve a cargar la aplicación, espera hasta 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 subsiguiente (comienza el generador de perfiles, haz clic en el botón, espera hasta que se complete la animación, detén el generador de perfiles).
Generación de perfiles de la GUI

Ejecutar solo la parte correcta de tu aplicación puede ser más difícil en un programa de GUI que cuando optimizas, por ejemplo, el rastreador de rayos de tu motor 3D. Por ejemplo, si deseas generar perfiles de lo que ocurre cuando haces clic en un botón, es posible que se activen eventos de desplazamiento del mouse que no estén relacionados en el proceso y que los resultados sean menos concluyentes. Intenta evitar eso :)

Interfaz programática

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

Comienza 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 la generación de perfiles, asegúrate de que realmente puedes reproducir los resultados. Solo entonces podrás saber si tus optimizaciones realmente mejoraron algo. Además, la generación de perfiles a nivel de 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 muchas otras cosas que suceden en tu computadora:

  1. Un temporizador no relacionado en tu propia aplicación que se activa mientras mides otra cosa.
  2. El recolector de basura está haciendo su trabajo.
  3. Otra pestaña del navegador que realiza un trabajo duro en el mismo subproceso operativo.
  4. Otro programa en tu computadora que está agotando la CPU y haciendo que tu aplicación sea más lenta.
  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 con mayor claridad.

Medir, mejorar, 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 generar el perfil. Si estás conforme con el resultado, continúa. Si no ves una mejora, es probable que debas revertir el cambio y no dejarlo en "porque no puede doler".

Estrategias de optimización

Minimiza la interacción del DOM

Un tema común para mejorar la velocidad de las aplicaciones cliente web es minimizar la interacción del DOM. Si bien la velocidad de los motores de JavaScript aumentó en orden de magnitud, el acceso al DOM no se volvió más rápido a la misma velocidad. Esto también se debe a razones muy prácticas que nunca sucederán (por ejemplo, diseñar y dibujar elementos en una pantalla solo toma tiempo).

Nodos de DOM en caché

Cuando recuperes un nodo o una lista de nodos del DOM, intenta pensar si podrías reutilizarlos en un cálculo posterior (o incluso en la próxima iteración de bucle). Siempre que no agregue ni borre nodos en el área relevante, esto suele ser el caso.

Antes:

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

Después:

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

Valores de atributos de la caché

De la misma manera en que puedes almacenar en caché los nodos del 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 varias veces.

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);

Mover la manipulación del DOM fuera de los bucles

Los bucles son a menudo puntos problemáticos para la optimización. Intenta pensar en formas de separar la computación de números real para trabajar con el DOM. A menudo, es posible hacer un cálculo y, luego, 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('');

Rediseños y reprocesamientos

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 recalcular porque tu código recientemente modificó algo relacionado en el DOM. Por lo tanto, se debe evitar combinar el acceso de lectura y escritura al DOM. Idealmente, el código siempre debería estar agrupado en dos fases:

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

Intenta no programar patrones como los siguientes:

  • Fase 1: Lee los valores del DOM
  • Fase 2: Modifica el DOM
  • Fase 3: Leer 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);
}

Estos consejos deben considerarse para las acciones que ocurren dentro de un contexto de ejecución de JavaScript. (p.ej., en un controlador de eventos, en un controlador de intervalos o cuando se administra una respuesta ajax).

Cuando se ejecuta la función paintSlow() desde arriba, se crea la siguiente imagen:

paintSlow()

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

Implementación más rápida

Estas imágenes muestran que reordenar la manera en que tu código accede al DOM puede mejorar notablemente el rendimiento de la representació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. Una optimización similar se puede aplicar básicamente a todo el código del "mundo real" y obtener resultados realmente espectaculares.

Más información: Renderización: nueva pintura, reprocesamiento/diseño, cambio de estilo de Stoyan Stefanov

Redibujos y bucle de eventos

La ejecución de JavaScript en el navegador sigue un modelo de “bucle de eventos”. De forma predeterminada, el navegador se encuentra en estado "inactivo". Este estado puede ser interrumpido por eventos de interacciones del usuario o, por ejemplo, temporizadores de JavaScript o devoluciones de llamada de Ajax. Cada vez que un fragmento de JavaScript se ejecuta en dicho punto de interrupción, el navegador generalmente espera a que finalice hasta que vuelva a renderizar la pantalla (es posible que haya excepciones para JavaScript de ejecución muy prolongada o en casos como cuadros de alerta que interrumpen la ejecución de JavaScript).

Consecuencias

  1. Si tus ciclos de animación de JavaScript tardan más de 1 a 30 segundos en ejecutarse, no podrás crear animaciones fluidas porque el navegador no volverá a pintar durante la ejecución de JS. Si esperas poder controlar los eventos de usuario, debes hacerlo mucho más rápido.
  2. A veces, es útil retrasar algunas acciones de JavaScript hasta un poco más tarde. P.ej., setTimeout(function() { ... }, 0) Esto le indica efectivamente al navegador que ejecute la devolución de llamada en cuanto el bucle de eventos vuelva a estar inactivo (de hecho, algunos navegadores esperarán al menos 10 ms). Debes tener en cuenta que esto creará dos ciclos de ejecución de JavaScript que son muy parecidos en el tiempo. Ambos podrían generar que se vuelva a pintar la pantalla, lo que podría duplicar el tiempo total dedicado a pintar. El hecho de que esto active realmente dos procesos de pintura depende de la heurística del navegador.

Versión estándar:

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

Agreguemos un poco de retraso:

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

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

Inicialización diferida

Los usuarios quieren apps web que se carguen rápido y se sientan responsivas. Sin embargo, los usuarios tienen diferentes umbrales en cuanto a lo que perciben como lento en función de la acción que realizan. Por ejemplo, una app nunca debe realizar muchos cálculos en un evento de desplazamiento del mouse, ya que esto podría generar una mala experiencia del usuario mientras este continúa moviendo el mouse. Sin embargo, los usuarios están acostumbrados a aceptar un poco de retraso después de hacer clic en un botón.

Por lo tanto, podría tener sentido mover tu código de inicialización para que se ejecute lo más tarde posible (por ejemplo, cuando el usuario hace clic en un botón que activa un componente 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

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

En este caso, la solución es usar una técnica llamada delegación de eventos. En lugar de adjuntar controladores de eventos individuales a los elementos, la naturaleza burbujeante de muchos eventos de navegador se utiliza cuando se adjunta el controlador de eventos a un nodo superior y se verifica el nodo objetivo del evento para ver si el evento es de interés.

En jQuery, esto se puede expresar con facilidad de la siguiente manera:

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

Cuándo no utilizar la delegación de eventos

En ocasiones, puede suceder lo contrario: si utilizas la delegación de eventos y tienes problemas 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 debe pagarse por cada invocación de ese evento. Esto puede ser costoso, especialmente para eventos que ocurren con frecuencia como "mouseover" o incluso "mousemove".

Problemas típicos y soluciones

Las actividades que hago en $(document).ready tardan mucho tiempo

El consejo personal de Malte: No hagas nada en $(document).ready. Intenta entregar el documento en su forma final. Muy bien, puedes registrar objetos de escucha de eventos, pero solo con el selector de ID o la delegación de eventos. Para eventos costosos, como "mousemove", retrasa el registro hasta que sean necesarios (evento de desplazamiento del mouse en el elemento relevante).

Y si realmente necesitas hacer cosas, como hacer una solicitud Ajax para obtener datos reales, entonces muestra una buena animación; es posible que desees incluir la animación como un URI de datos si es un GIF animado o algo similar.

Como agregué una película Flash a la página, todo está muy lento.

Agregar Flash a una página siempre ralentiza la representación porque el diseño final de la ventana debe ser "negociado" entre el navegador y el complemento de Flash. Cuando no puedas evitar por completo colocar Flash en tus páginas, asegúrate de establecer el parámetro de Flash "wmode" en el valor "window" (que es el valor predeterminado). Esto inhabilitará la capacidad de combinar elementos HTML y Flash (no podrás ver un elemento HTML sobre la película Flash ni la película Flash no puede ser transparente). Esto puede ser un inconveniente, pero mejorará drásticamente tu rendimiento. Por ejemplo, echa un vistazo a la forma en que youtube.com evita cuidadosamente colocar capas sobre el reproductor de películas principal.

Estoy guardando elementos en localStorage, ahora mi aplicación se entrecorta.

La escritura en localStorage es una operación síncrona que implica iniciar el disco duro. No querrás realizar operaciones síncronas "de larga duración" mientras realizas animaciones. Mueve el acceso a localStorage a un lugar de tu código donde puedas asegurarte de que el usuario está inactivo y de que no se muestran animaciones.

La generación de perfiles señala que un selector de jQuery es muy lento

Primero, asegúrate de que el selector se pueda ejecutar a través de document.querySelectorAll. Puedes probarlo en la Consola de JavaScript. Si hay una excepción, reescribe el selector para no usar ninguna extensión especial de tu framework de JavaScript. Esto acelerará el selector en navegadores modernos en un orden de magnitud.

Si esto no ayuda o si también deseas ser rápido en los navegadores modernos, sigue estos lineamientos:

  • Sé lo más específico posible en el lado derecho del selector.
  • Utiliza un nombre de etiqueta que no utilices con frecuencia como la parte del selector que se encuentra más a la derecha.
  • Si nada ayuda, piensa en reescribir las cosas para que puedas usar un selector de ID.

Todas estas manipulaciones de DOM tardan mucho tiempo

Varias inserciones, eliminaciones y actualizaciones de nodos del DOM pueden ser muy lentas. Por lo general, esto se puede optimizar generando una gran cadena de código HTML y usando domNode.innerHTML = newHTML para reemplazar el contenido anterior. Ten en cuenta que esto puede ser muy malo para el mantenimiento y podría crear vínculos de memoria en IE, por lo que debes tener cuidado.

Otro problema común es que tu código de inicialización puede crear mucho HTML. Por ejemplo, un complemento de jQuery que transforma un cuadro de selección en un conjunto de divs porque eso es lo que los usuarios de diseño querían sin conocer las prácticas recomendadas de UX. Si realmente quieres que tu página sea rápida, nunca lo hagas. En cambio, entrega todo el lenguaje de marcado del servidor en su forma final. Esto tiene muchos problemas, así que piensa bien si la velocidad vale la pena.

Herramientas

  1. JSPerf: Compara pequeños fragmentos de JavaScript
  2. Firebug: Para perfilar en Firefox
  3. Herramientas para desarrolladores de Google Chrome (disponibles como WebInspector en Safari)
  4. DOM Monster: Para optimizar el rendimiento de DOM
  5. DynaTrace Ajax Edition: Para optimizar la generación de perfiles y la pintura en Internet Explorer

Lecturas adicionales

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