El proceso de renderizado de la página implica varios pasos: primero, JavaScript ejecuta animaciones o manipula el DOM para interacciones. Luego, se calculan los estilos; si hay cambios, se recalculan y se devuelven al DOM. A continuación, el navegador realiza un reflujo o layout, recalculando la posición y tamaño de los elementos. Después, ocurre el repintado o paint, donde se renderizan los elementos en las capas de renderizado. Finalmente, se compone o composites las capas para mostrar la página al usuario.
Las operaciones de JavaScript que afectan el rendimiento incluyen accceder y modificar el DOM, cambiar estilos que causan repintado o reflujo, y manejar eventos para interacciones del usuario.
El reflujo ocurre cuando la posición o tamaño de un elemento cambia, requiriendo que el navegador recalcule el árbol de renderizado y redibuje los elementos afectados. El repintado ocurre cuando solo cambian estilos sin alterar el tamaño o posición. Siempre que hay reflujo, también hay repintado, pero no viceversa.
Operaciones que causan reflujo incluyen cambios en contenido como texto o dimensiones de imágenes, alteraciones en atributos geométricos como ancho o alto, modificaciones en la estructura del árbol DOM como agregar o eliminar nodos, y consultas a propiedades de layout como offsetTop o scrollTop. Cambios en el tamaño de la ventana del navegador también pueden desencadenar reflujo.
Para optimizar, los navegadores usan una cola de operaciones de reflujo y repintado, procesándolas en lotes. Sin embargo, consultar propiedades como offsetTop puede forzar una ejecución inmediata. Para minimizar el impacto, es fundamental reducir las manipulaciones del árbol de renderizado y las solicitudes de información de estilo.
Se pueden combinar cambios de estilo en una sola operación. Por ejemplo, en lugar de establecer propiedades individualmente, se puede usar cssText o cambiar clases CSS:
// Usando cssText
const miElemento = document.querySelector('#contenedor');
miElemento.style.cssText = 'color: azul; height: 150px; width: 50px';
// Usando clases
// En CSS: .activo { color: azul; width: 50px; height: 150px; }
const elementoActivo = document.getElementById('activo');
elementoActivo.classList.add('activo');
Para modificaciones masivas del DOM, se puede aislar el elemento del flujo del documento, realizar cambios y reintegrarlo. Esto incluye ocultar el elemento temporalmente, usar posición absoluta o fija, o crear fragmentos de documento para actualizaciones por lotes. Por ejemplo, construir una lista en memoria antes de insertarla:
// Crear fragmento de documento
const fragmento = document.createDocumentFragment();
for (let i = 0; i < 5; i++) {
const nuevoItem = document.createElement('li');
nuevoItem.textContent = `Ítem ${i + 1}`;
fragmento.appendChild(nuevoItem);
}
document.getElementById('lista').appendChild(fragmento);
Además, es útil almacenar en caché información de layout para evitar consultas repetidas. Asignar valores a variables locales reduce la necesidad de recalcular propiedades:
// Cachear valor de layout
const anchoActual = elementoBloque.offsetWidth;
elementoBloque.style.width = (anchoActual + 20) + 'px';