Virtualiza listas grandes con el CDK de Angular

Implementa el desplazamiento virtual para lograr que las listas grandes sean más responsivas.

Stephen Fluin
Stephen Fluin

La lista de desplazamiento es uno de los patrones de IU más comunes en la actualidad, ya sea navegar por un feed de desplazamiento infinito en tu sitio de redes sociales favorito o navegar por un panel empresarial. Cuando el desplazamiento por listas se vuelve muy largo (cientos, miles o cientos de miles de elementos), el rendimiento de la aplicación puede verse afectado.

Las listas grandes pueden tardar en cargarse porque la aplicación debe cargar y renderizar todos los datos por adelantado. También pueden ser lentos para renderizarse y navegar porque cada elemento de la lista puede tener datos enriquecidos, contenido multimedia y funcionalidad.

Los usuarios pueden experimentar problemas cuando cargan o se desplazan por la página, lo que lleva a frustración y abandono de la página.

Desplazamiento virtual en Angular con el kit de desarrollo de componentes

El desplazamiento virtual es la técnica principal que se usa para abordar estos problemas de escala. El desplazamiento virtual da la impresión de una lista muy grande, ya que proporciona una barra de desplazamiento del tamaño adecuado, y la capacidad de navegar por ella sin que la aplicación tenga que retener toda la lista en la memoria o renderizarla en la página.

En Angular, el kit de desarrollo de componentes (CDK) proporciona el desplazamiento virtual. Al modificar la manera en que iteras a través de las listas y proporcionar algunos parámetros de configuración adicionales, el desplazamiento virtual del CDK administrará automáticamente la renderización virtual de tus listas, lo que mejorará el rendimiento y la capacidad de respuesta de la página.

En lugar de renderizar toda la lista a la vez, solo se renderizará un subconjunto de los elementos que se ajusten a la pantalla (más un búfer pequeño). A medida que el usuario navega, se calcula y renderiza un nuevo subconjunto de elementos, y se reutiliza el DOM existente si así lo desea.

En el resto de esta publicación, se explica cómo configurar el desplazamiento virtual básico. Puedes ver un ejemplo funcional completo en esta app de ejemplo:

Configurar el desplazamiento virtual

Primero, asegúrate de haber instalado @angular/cdk con tu administrador de paquetes favorito. Para instalarlo con npm, ejecuta el siguiente comando en la terminal:

npm install --save @angular/cdk

Agrega ScrollingModule a tu app

Con el CDK instalado, importa ScrollingModule, que se encarga del desplazamiento virtual, desde el paquete @angular/cdk/scrolling. Luego, agrégala al array de importaciones de tu módulo:

import {ScrollingModule} from '@angular/cdk/scrolling';

...
imports: [
  ScrollingModule
...
]
...

Crea un viewport

Para ver cómo funciona el paquete, intenta crear un componente con una lista simple de números del 0 al 99,999:

@Component({
  template: `<div *ngFor="let item of list">{{item}}</div>`
})
export class ScrollComponent {
  list = Array.from({length: 100000}).map((_, i) => i);
}

Cuando el navegador procesa la app, tiene que renderizar 100,000 elementos individuales <div>. Esto podría estar bien para nodos de texto simples, pero cualquier complejidad en la plantilla repetida no se escalará bien y los objetos de escucha de eventos se multiplicarán de manera significativa.

Para agregar desplazamiento virtual y evitar esos problemas, debes crear un viewport uniendo la lista en un elemento <cdk-virtual-scroll-viewport>:

@Component({
  template: `<cdk-virtual-scroll-viewport>
    <div *ngFor="let item of list">{{item}}</div>
    </cdk-virtual-scroll-viewport>`
})
export class ScrollComponent {
  list = Array.from({length: 100000}).map((_, i) => i);
}

Dado que ScrollingModule renderiza subconjuntos de la lista de forma dinámica, debes especificar la altura del viewport mediante CSS estándar. También debes especificar itemSize para sugerirle al viewport una sugerencia de su contenido. El módulo usa esta información para determinar cuántos elementos se conservarán en el DOM en un momento determinado y cómo renderizar una barra de desplazamiento del tamaño adecuado.

@Component({
  template: `<cdk-virtual-scroll-viewport itemSize="18" style="height:80vh">
    <div *ngFor="let item of list">{{item}}</div>
    </cdk-virtual-scroll-viewport>`
})
export class ScrollComponent {
  list = Array.from({length: 100000}).map((_, i) => i);
}

Por último, convierte *ngFor en *cdkVirtualFor:

@Component({
  template: `<cdk-virtual-scroll-viewport itemSize="18" style="height:80vh">
    <div *cdkVirtualFor="let item of list">{{item}}</div>
    </cdk-virtual-scroll-viewport>`
})
export class ScrollComponent {
  list = Array.from({length: 100000}).map((_, i) => i);
}

En lugar de iterar a través de toda la lista, el viewport identificará y realizará de forma dinámica el subconjunto correcto de la lista para el usuario. Ahora, cuando el usuario carga la página, el CDK debe renderizar el subconjunto de la lista que se ajusta a la pantalla (más un poco del búfer), y cualquier evento de desplazamiento en el viewport cargará y renderizará el subconjunto adecuado de la lista:

El CDK que renderiza subconjuntos de una lista a medida que el usuario se desplaza.

Un paso más allá

Las capacidades de desplazamiento virtual del CDK van mucho más allá de este ejemplo básico. En la app de ejemplo, la lista completa estaba en la memoria, pero se podía recuperar a pedido para aplicaciones más complejas. Para obtener más información sobre las demás funciones de ScrollingModule y la directiva cdkVirtualOf, lee sobre Scrolling en la documentación del CDK.

Hero image de Mr Cup / Fabien Barral en Unsplash.