ListView JavaScript Performante nel 2026: Windowing, Virtualizzazione e Ottimizzazione delle Prestazioni

Scopri come creare ListView JavaScript ad alte prestazioni con windowing e virtualizzazione. TanStack Virtual, React Virtualized e tecniche avanzate per il 2026.

ListView JavaScript Performante nel 2026: Windowing, Virtualizzazione e Ottimizzazione delle Prestazioni

Le applicazioni web moderne devono spesso visualizzare migliaia o addirittura centinaia di migliaia di record in liste. Che si tratti di cataloghi e-commerce, feed di social media o dashboard amministrative, le prestazioni di una ListView possono influenzare drasticamente l'esperienza utente. Questo articolo esplora le tecniche più recenti per ottimizzare le ListView JavaScript nel 2026.

La virtualizzazione può ridurre il consumo di memoria fino al 95% per liste con oltre 10.000 elementi, garantendo al contempo uno scrolling fluido.

Il Problema delle Liste di Grandi Dimensioni

Quando un'applicazione renderizza una lista con diecimila elementi, il browser crea nodi DOM per ciascuno di essi. Questo comporta problemi significativi:

  • Elevato consumo di memoria: Ogni nodo DOM richiede memoria
  • Tempo di rendering iniziale lento: Creare migliaia di elementi richiede tempo
  • Prestazioni di scrolling scadenti: Il browser deve gestire tutti gli elementi
  • Layout thrashing: Ricalcoli frequenti del layout
typescript
// Implementazione naive - NON consigliata per liste grandi
function NaiveList({ items }: { items: Item[] }) {
  return (
    <div className="list-container">
      {items.map((item) => (
        <div key={item.id} className="list-item">
          <h3>{item.title}</h3>
          <p>{item.description}</p>
        </div>
      ))}
    </div>
  );
}

Windowing come Soluzione

Il windowing, noto anche come "scrolling virtuale", è una tecnica in cui vengono renderizzati solo gli elementi visibili più un piccolo buffer sopra e sotto. Gli elementi rimanenti vengono creati solo quando necessario.

typescript
import { useVirtualizer } from '@tanstack/react-virtual';

function VirtualizedList({ items }: { items: Item[] }) {
  const parentRef = useRef<HTMLDivElement>(null);

  const virtualizer = useVirtualizer({
    count: items.length,
    getScrollElement: () => parentRef.current,
    estimateSize: () => 80,
    overscan: 5,
  });

  return (
    <div ref={parentRef} className="h-[600px] overflow-auto">
      <div
        style={{
          height: `${virtualizer.getTotalSize()}px`,
          width: '100%',
          position: 'relative',
        }}
      >
        {virtualizer.getVirtualItems().map((virtualItem) => (
          <div
            key={virtualItem.key}
            style={{
              position: 'absolute',
              top: 0,
              left: 0,
              width: '100%',
              height: `${virtualItem.size}px`,
              transform: `translateY(${virtualItem.start}px)`,
            }}
          >
            <ListItem item={items[virtualItem.index]} />
          </div>
        ))}
      </div>
    </div>
  );
}

TanStack Virtual: Lo Standard Moderno

TanStack Virtual (precedentemente React Virtual) si è affermato nel 2026 come la soluzione principale per la virtualizzazione nelle applicazioni React. La libreria offre funzionalità core framework-agnostic con adapter specifici per React, Vue, Solid e altri framework.

typescript
import { useVirtualizer, VirtualizerOptions } from '@tanstack/react-virtual';

interface ListProps<T> {
  items: T[];
  renderItem: (item: T, index: number) => React.ReactNode;
  itemHeight?: number | ((index: number) => number);
  overscan?: number;
}

function HighPerformanceList<T extends { id: string }>({
  items,
  renderItem,
  itemHeight = 80,
  overscan = 10
}: ListProps<T>) {
  const parentRef = useRef<HTMLDivElement>(null);

  const virtualizer = useVirtualizer({
    count: items.length,
    getScrollElement: () => parentRef.current,
    estimateSize: typeof itemHeight === 'function'
      ? itemHeight
      : () => itemHeight,
    overscan,
    measureElement: (element) => element.getBoundingClientRect().height,
  });

  const virtualItems = virtualizer.getVirtualItems();

  return (
    <div
      ref={parentRef}
      className="h-full overflow-auto will-change-transform"
    >
      <div
        className="relative w-full"
        style={{ height: `${virtualizer.getTotalSize()}px` }}
      >
        {virtualItems.map((virtualItem) => (
          <div
            key={items[virtualItem.index].id}
            ref={virtualizer.measureElement}
            data-index={virtualItem.index}
            className="absolute top-0 left-0 w-full"
            style={{
              transform: `translateY(${virtualItem.start}px)`,
            }}
          >
            {renderItem(items[virtualItem.index], virtualItem.index)}
          </div>
        ))}
      </div>
    </div>
  );
}

Altezze Dinamiche degli Elementi

Una delle sfide maggiori nella virtualizzazione sono le liste con elementi di altezza variabile. TanStack Virtual offre la funzione measureElement che misura la dimensione effettiva di ogni elemento.

typescript
function DynamicHeightList({ items }: { items: Post[] }) {
  const parentRef = useRef<HTMLDivElement>(null);
  const measurementsCache = useRef<Map<string, number>>(new Map());

  const virtualizer = useVirtualizer({
    count: items.length,
    getScrollElement: () => parentRef.current,
    estimateSize: (index) => {
      const cached = measurementsCache.current.get(items[index].id);
      if (cached) return cached;
      // Stima basata sulla lunghezza del contenuto
      const contentLength = items[index].content.length;
      return Math.max(100, Math.min(400, contentLength * 0.5));
    },
    measureElement: (element) => {
      const height = element.getBoundingClientRect().height;
      const id = element.getAttribute('data-id');
      if (id) {
        measurementsCache.current.set(id, height);
      }
      return height;
    },
    overscan: 5,
  });

  return (
    <div ref={parentRef} className="h-screen overflow-auto">
      <div
        style={{
          height: `${virtualizer.getTotalSize()}px`,
          position: 'relative',
        }}
      >
        {virtualizer.getVirtualItems().map((virtualItem) => {
          const item = items[virtualItem.index];
          return (
            <div
              key={item.id}
              ref={virtualizer.measureElement}
              data-index={virtualItem.index}
              data-id={item.id}
              style={{
                position: 'absolute',
                top: 0,
                left: 0,
                width: '100%',
                transform: `translateY(${virtualItem.start}px)`,
              }}
            >
              <PostCard post={item} />
            </div>
          );
        })}
      </div>
    </div>
  );
}

Infinite Scrolling con Virtualizzazione

La combinazione di virtualizzazione con infinite scrolling consente il caricamento e la visualizzazione di quantità illimitate di dati con prestazioni ottimali.

typescript
import { useInfiniteQuery } from '@tanstack/react-query';
import { useVirtualizer } from '@tanstack/react-virtual';

function InfiniteVirtualList() {
  const parentRef = useRef<HTMLDivElement>(null);

  const {
    data,
    fetchNextPage,
    hasNextPage,
    isFetchingNextPage,
  } = useInfiniteQuery({
    queryKey: ['items'],
    queryFn: ({ pageParam = 0 }) => fetchItems(pageParam),
    getNextPageParam: (lastPage) => lastPage.nextCursor,
    initialPageParam: 0,
  });

  const allItems = useMemo(
    () => data?.pages.flatMap((page) => page.items) ?? [],
    [data]
  );

  const virtualizer = useVirtualizer({
    count: hasNextPage ? allItems.length + 1 : allItems.length,
    getScrollElement: () => parentRef.current,
    estimateSize: () => 80,
    overscan: 10,
  });

  const virtualItems = virtualizer.getVirtualItems();

  useEffect(() => {
    const lastItem = virtualItems[virtualItems.length - 1];
    if (!lastItem) return;

    if (
      lastItem.index >= allItems.length - 1 &&
      hasNextPage &&
      !isFetchingNextPage
    ) {
      fetchNextPage();
    }
  }, [virtualItems, allItems.length, hasNextPage, isFetchingNextPage, fetchNextPage]);

  return (
    <div ref={parentRef} className="h-[600px] overflow-auto">
      <div
        style={{
          height: `${virtualizer.getTotalSize()}px`,
          position: 'relative',
        }}
      >
        {virtualItems.map((virtualItem) => {
          const isLoader = virtualItem.index >= allItems.length;

          return (
            <div
              key={virtualItem.key}
              style={{
                position: 'absolute',
                top: 0,
                left: 0,
                width: '100%',
                height: `${virtualItem.size}px`,
                transform: `translateY(${virtualItem.start}px)`,
              }}
            >
              {isLoader ? (
                <div className="flex items-center justify-center h-full">
                  <Spinner />
                </div>
              ) : (
                <ItemRow item={allItems[virtualItem.index]} />
              )}
            </div>
          );
        })}
      </div>
    </div>
  );
}

Ottimizzazioni delle Prestazioni

Oltre alla virtualizzazione, esistono altre tecniche per migliorare le prestazioni delle ListView.

Memoization degli Elementi della Lista

typescript
const MemoizedListItem = memo(
  function ListItem({ item }: { item: Item }) {
    return (
      <div className="p-4 border-b border-gray-200">
        <h3 className="font-medium">{item.title}</h3>
        <p className="text-gray-600">{item.description}</p>
      </div>
    );
  },
  (prevProps, nextProps) => prevProps.item.id === nextProps.item.id
);

Ottimizzazioni CSS

css
.list-container {
  contain: strict;
  will-change: transform;
}

.list-item {
  contain: layout style paint;
  content-visibility: auto;
  contain-intrinsic-size: 0 80px;
}

Content-Visibility per Liste Non Virtualizzate

typescript
function OptimizedStaticList({ items }: { items: Item[] }) {
  return (
    <div className="list-container">
      {items.map((item) => (
        <div
          key={item.id}
          className="list-item"
          style={{
            contentVisibility: 'auto',
            containIntrinsicSize: '0 80px',
          }}
        >
          <ListItemContent item={item} />
        </div>
      ))}
    </div>
  );
}

Virtualizzazione Orizzontale e Griglie

TanStack Virtual supporta anche la virtualizzazione orizzontale e griglie bidimensionali.

typescript
function VirtualizedGrid({ items, columns = 4 }: { items: Item[]; columns?: number }) {
  const parentRef = useRef<HTMLDivElement>(null);
  const rows = Math.ceil(items.length / columns);

  const rowVirtualizer = useVirtualizer({
    count: rows,
    getScrollElement: () => parentRef.current,
    estimateSize: () => 200,
    overscan: 2,
  });

  const columnVirtualizer = useVirtualizer({
    horizontal: true,
    count: columns,
    getScrollElement: () => parentRef.current,
    estimateSize: () => 250,
    overscan: 1,
  });

  return (
    <div ref={parentRef} className="h-[600px] overflow-auto">
      <div
        style={{
          height: `${rowVirtualizer.getTotalSize()}px`,
          width: `${columnVirtualizer.getTotalSize()}px`,
          position: 'relative',
        }}
      >
        {rowVirtualizer.getVirtualItems().map((virtualRow) => (
          <Fragment key={virtualRow.key}>
            {columnVirtualizer.getVirtualItems().map((virtualColumn) => {
              const itemIndex = virtualRow.index * columns + virtualColumn.index;
              if (itemIndex >= items.length) return null;

              return (
                <div
                  key={virtualColumn.key}
                  style={{
                    position: 'absolute',
                    top: 0,
                    left: 0,
                    width: `${virtualColumn.size}px`,
                    height: `${virtualRow.size}px`,
                    transform: `translateX(${virtualColumn.start}px) translateY(${virtualRow.start}px)`,
                  }}
                >
                  <GridItem item={items[itemIndex]} />
                </div>
              );
            })}
          </Fragment>
        ))}
      </div>
    </div>
  );
}

Pronto a superare i tuoi colloqui su Node.js / NestJS?

Pratica con i nostri simulatori interattivi, flashcards e test tecnici.

Debugging e Monitoring

Per identificare problemi di prestazioni, è possibile monitorare diverse metriche.

typescript
function useVirtualListMetrics(virtualizer: Virtualizer<HTMLDivElement, Element>) {
  const [metrics, setMetrics] = useState({
    renderedCount: 0,
    totalCount: 0,
    scrollOffset: 0,
    visibleRange: { start: 0, end: 0 },
  });

  useEffect(() => {
    const updateMetrics = () => {
      const virtualItems = virtualizer.getVirtualItems();
      setMetrics({
        renderedCount: virtualItems.length,
        totalCount: virtualizer.options.count,
        scrollOffset: virtualizer.scrollOffset ?? 0,
        visibleRange: {
          start: virtualItems[0]?.index ?? 0,
          end: virtualItems[virtualItems.length - 1]?.index ?? 0,
        },
      });
    };

    updateMetrics();
    const unsubscribe = virtualizer.scrollElement?.addEventListener(
      'scroll',
      updateMetrics
    );

    return () => {
      virtualizer.scrollElement?.removeEventListener('scroll', updateMetrics as any);
    };
  }, [virtualizer]);

  return metrics;
}

Conclusione

L'implementazione di ListView performanti in JavaScript nel 2026 richiede una solida comprensione delle tecniche di virtualizzazione. TanStack Virtual offre una soluzione matura e type-safe che si integra perfettamente nelle applicazioni React moderne. Combinando il windowing con memoization, CSS containment e strategie di caricamento intelligenti, è possibile visualizzare in modo fluido anche liste con centinaia di migliaia di elementi. Le tecniche presentate sono essenziali per qualsiasi applicazione web data-intensive e dovrebbero far parte del repertorio standard dello sviluppo frontend moderno.

Sfida del giorno

Sapresti trovare il bug in Node.js / NestJS?

Uno snippet reale, un bug nascosto, un tentativo al giorno. Senza account per provare.

Anthony Fillion-Maillet

Scritto da

Anthony Fillion-Maillet

Fondatore di SharpSkill

Sviluppatore fullstack da oltre 10 anni. Guida SharpSkill e risponde di tutto ciò che vi viene pubblicato.

Aggiornato il 15 settembre 2026

Condividi

Articoli correlati