Virtual Scrolling para Interfaces de Alto Rendimiento
Renderizar 500,000 filas en un navegador probablemente congelará, ralentizará o bloqueará tu interfaz. Aunque los navegadores modernos pueden manejar árboles DOM grandes, el rendimiento a menudo se degrada drásticamente a medida que crece el número de nodos debido a los costos de diseño, cálculo de estilos y memoria. El virtual scrolling resuelve esto renderizando únicamente lo que el usuario puede ver realmente — y esa única restricción cambia por completo el rendimiento de las interfaces con grandes volúmenes de datos.
Puntos Clave
- El virtual scrolling renderiza solo los elementos visibles en el viewport (más un pequeño buffer), manteniendo constante el número de nodos DOM independientemente del tamaño del conjunto de datos.
- Funciona calculando los índices visibles desde
scrollTop, renderizando esos elementos y usando elementos de padding para simular la altura total desplazable. - Las alturas fijas de elementos mantienen la implementación simple. Las alturas dinámicas requieren almacenamiento en caché de mediciones y corrección cuidadosa de la posición de scroll.
- La búsqueda nativa del navegador (Ctrl+F), la accesibilidad y la estabilidad de la posición de scroll requieren atención adicional en listas virtualizadas.
- Existen bibliotecas maduras para React, Angular y Vue — construir desde cero rara vez es necesario en producción.
¿Qué es el Virtual Scrolling (y por qué no es Infinite Scroll)?
Virtual scrolling (también llamado virtualización de listas o windowing) renderiza únicamente los elementos actualmente visibles en el viewport, más un pequeño buffer arriba y abajo. A medida que el usuario se desplaza, los elementos que salen del viewport se eliminan del DOM y se insertan nuevos en su lugar. El conjunto de datos completo nunca entra completamente en el DOM.
Esto es fundamentalmente diferente del infinite scroll. El infinite scroll añade elementos al DOM a medida que te desplazas — la lista sigue creciendo. El virtual scrolling intercambia elementos dentro y fuera, manteniendo el número de nodos DOM aproximadamente constante independientemente del tamaño del conjunto de datos.
La diferencia práctica es significativa. Una lista renderizada ingenuamente de 100,000 elementos podría crear más de 100,000 nodos DOM en memoria. Una lista virtualizada del mismo conjunto de datos podría mantener entre 50 y 80 nodos en cualquier momento dado.
Cómo Funcionan Conceptualmente las Listas Virtualizadas
El mecanismo se basa en algunas ideas sencillas que trabajan juntas:
La ventana del viewport. Le das al contenedor de scroll una altura fija. Esto define cuántos elementos son visibles a la vez — llamémoslo visibleCount = Math.ceil(containerHeight / itemHeight).
Cálculo de índices. A medida que el usuario se desplaza, lees scrollTop para determinar qué elemento está en la parte superior del área visible: startIndex = Math.floor(scrollTop / itemHeight). El índice final sigue: endIndex = startIndex + visibleCount.
Ilusión de posición de scroll. Si solo renderizas 50 elementos, la barra de desplazamiento reflejaría una lista diminuta. Para simular la altura completa, colocas un elemento de padding vacío arriba de los elementos renderizados (altura = startIndex × itemHeight) y otro abajo (altura = espacio restante). La barra de desplazamiento se comporta como si el conjunto de datos completo estuviera presente.
Overscan (buffer). Renderizar solo los elementos exactamente visibles causa un efecto de aparición brusca durante el desplazamiento rápido. El overscan renderiza algunas filas adicionales arriba y abajo del viewport — típicamente entre 5 y 10 elementos, dependiendo del caso de uso — para que los elementos ya estén en el DOM antes de deslizarse a la vista.
Alturas de Elementos Fijas vs. Dinámicas
La virtualización de altura fija es directa y confiable. Cada cálculo es aritmética simple.
Las alturas dinámicas son significativamente más difíciles. Necesitas medir cada elemento después del renderizado y almacenar en caché esas mediciones, o estimar las alturas por adelantado y corregirlas después de la medición. Ambos enfoques añaden complejidad y pueden causar inestabilidad en la posición de scroll si no se manejan con cuidado. Si tu caso de uso lo permite, vale la pena diseñar hacia alturas fijas.
Discover how at OpenReplay.com.
Compromisos del Mundo Real a Esperar
El virtual scrolling no es gratuito. Algunas cosas se rompen o requieren trabajo adicional:
- La búsqueda de texto del navegador (Ctrl+F) deja de funcionar de manera confiable porque la mayor parte del contenido no está en el DOM. Necesitarás implementar tu propia búsqueda.
- La accesibilidad requiere atención. Aplica
role="list",role="feed", orole="grid"al contenedor. Puedes usar atributos comoaria-setsizeyaria-posinsetpara que las tecnologías asistivas puedan entender el tamaño completo de la lista y la posición de cada elemento. Mantén la gestión del foco para que la navegación por teclado no se rompa cuando los elementos se desmonten. Un pequeño buffer de overscan también ayuda a los lectores de pantalla a detectar que existe más contenido. - La estabilidad de la posición de scroll se vuelve complicada cuando los datos se actualizan dinámicamente — los elementos añadidos o eliminados por encima de la posición actual de scroll pueden causar saltos bruscos.
Soporte del Ecosistema en Diferentes Frameworks
Rara vez necesitas construir esto desde cero en producción. Las bibliotecas maduras manejan los casos extremos:
- React: TanStack Virtual (headless, flexible) y react-window (ligero, soporta tamaños fijos y variables con configuración adicional)
- Angular: CDK Virtual Scroll está integrado en el Angular Component Dev Kit
- Vue: vue-virtual-scroller cubre la mayoría de los patrones comunes
Una alternativa CSS que vale la pena conocer: content-visibility: auto permite al navegador omitir el renderizado de contenido fuera de pantalla sin JavaScript. Puede mejorar el rendimiento de pintado en listas moderadas, pero no reduce el número de nodos DOM y no es un sustituto de la virtualización completa en conjuntos de datos grandes.
Cuándo Usarlo Realmente
El virtual scrolling añade complejidad. Vale la pena cuando:
- Tu lista supera unos cientos de elementos y el rendimiento del scroll está notablemente degradado
- Estás construyendo tablas, visores de logs, feeds o interfaces estilo hoja de cálculo
- El uso de memoria es una restricción (dispositivos móviles, sesiones de larga duración)
Para listas cortas, la paginación o el lazy loading simple a menudo es más sencillo y suficientemente bueno.
Conclusión
Los usuarios no necesitan 100,000 nodos DOM — necesitan sentir que pueden desplazarse a través de 100,000 elementos. El virtual scrolling ofrece esa sensación a una fracción del costo de renderizado. Al renderizar solo la porción visible de un conjunto de datos e intercambiar elementos dentro y fuera a medida que el usuario se desplaza, mantienes bajos los números de nodos DOM, el uso de memoria predecible y las tasas de fotogramas suaves. Los compromisos — Ctrl+F roto, consideraciones de accesibilidad, gestión de la posición de scroll — son reales pero bien entendidos, y el ecosistema de bibliotecas en React, Angular y Vue maneja la mayoría de ellos de forma predeterminada. Si tu lista es lo suficientemente grande como para afectar el rendimiento, la virtualización es la herramienta más efectiva disponible.
Preguntas Frecuentes
Sí, pero requiere cuidado adicional. La mayoría de las bibliotecas de virtualización asumen un diseño de lista de una sola columna. Para diseños basados en grid, necesitas calcular filas y columnas visibles juntas, teniendo en cuenta los elementos por fila. TanStack Virtual soporta virtualización de grid de forma nativa. Con otras bibliotecas, puede que necesites tratar cada fila como un único elemento virtualizado que contiene múltiples celdas.
Los rastreadores de motores de búsqueda típicamente no se desplazan a través del contenido, por lo que los elementos fuera del renderizado inicial no serán indexados. Si el SEO es importante para el contenido de tu lista, considera salida HTML paginada o alternativas amigables para rastreadores. Si renderizas contenido del lado del servidor, aplica la virtualización solo después de la hidratación en el cliente.
Esto generalmente significa que tu buffer de overscan es demasiado pequeño o que el renderizado de tus elementos es demasiado lento. Aumenta el número de overscan para que más elementos fuera de pantalla sean pre-renderizados. También verifica si los elementos de tu lista desencadenan recálculos de diseño costosos o cargan imágenes de forma sincrónica. Simplificar los componentes de elementos y usar contenido placeholder para imágenes puede reducir significativamente los fotogramas en blanco.
Sí. Antes de la navegación, guarda el valor actual de scrollTop y el startIndex correspondiente en el estado o session storage. Cuando el usuario regrese, restaura la posición del contenedor de scroll al valor guardado de scrollTop. La mayoría de las bibliotecas de virtualización exponen un método scrollToIndex o scrollToOffset que hace que esto sea sencillo de implementar al volver a montar.
Understand every bug
Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.