Tabelle ed elenchi molto grandi possono rallentare notevolmente le prestazioni del sito. La virtualizzazione può aiutarti.
react-window
è una libreria che consente di eseguire il rendering di elenchi di grandi dimensioni in modo efficiente.
Ecco un esempio di un elenco contenente 1000 righe visualizzate con
react-window
. Prova a scorrere il più velocemente possibile.
Perché è utile?
A volte potrebbe essere necessario visualizzare una tabella o un elenco di grandi dimensioni contenente molte righe. Il caricamento di ogni singolo elemento di un elenco di questo tipo può influire in modo significativo sul rendimento.
La virtualizzazione degli elenchi, o "finestre", è il concetto di rendering solo di ciò che è visibile all'utente. Il numero di elementi visualizzati inizialmente è un sottoinsieme molto piccolo dell'intero elenco e la "finestra" dei contenuti visibili si sposta quando l'utente continua a scorrere. In questo modo, migliorano sia il rendering sia le prestazioni di scorrimento dell'elenco.

I nodi DOM che escono dalla "finestra" vengono riciclati o sostituiti immediatamente con elementi più recenti man mano che l'utente scorre l'elenco verso il basso. In questo modo, il numero di tutti gli elementi visualizzati è specifico per le dimensioni della finestra.
react-window
react-window
è una piccola libreria di terze parti che semplifica la creazione di elenchi virtualizzati nella tua applicazione. Fornisce una serie di API di base
che possono essere utilizzate per diversi tipi di elenchi e tabelle.
Quando utilizzare gli elenchi di dimensioni fisse
Utilizza il componente FixedSizeList
se hai un elenco lungo e unidimensionale
di elementi di dimensioni uguali.
import React from 'react';
import { FixedSizeList } from 'react-window';
const items = [...] // some list of items
const Row = ({ index, style }) => (
<div style={style}>
{/* define the row component using items[index] */}
</div>
);
const ListComponent = () => (
<FixedSizeList
height={500}
width={500}
itemSize={120}
itemCount={items.length}
>
{Row}
</FixedSizeList>
);
export default ListComponent;
- Il componente
FixedSizeList
accetta le proprietàheight
,width
eitemSize
per controllare le dimensioni degli elementi all'interno dell'elenco. - Una funzione che esegue il rendering delle righe viene passata come elemento secondario a
FixedSizeList
. È possibile accedere ai dettagli del particolare elemento con l'argomentoindex
(items[index]
). - Viene passato anche un parametro
style
al metodo di rendering della riga che deve essere allegato all'elemento riga. Gli elementi dell'elenco sono posizionati in modo assoluto con i valori di altezza e larghezza assegnati in linea e il parametrostyle
è responsabile di questo.
L'esempio di Glitch mostrato in precedenza in questo articolo mostra un esempio di componente
FixedSizeList
.
Quando utilizzare elenchi di dimensioni variabili
Utilizza il componente VariableSizeList
per visualizzare un elenco di articoli di
dimensioni diverse. Questo componente funziona allo stesso modo di un elenco di dimensioni fisse, ma
si aspetta una funzione per la proprietà itemSize
anziché un valore specifico.
import React from 'react';
import { VariableSizeList } from 'react-window';
const items = [...] // some list of items
const Row = ({ index, style }) => (
<div style={style}>
{/* define the row component using items[index] */}
</div>
);
const getItemSize = index => {
// return a size for items[index]
}
const ListComponent = () => (
<VariableSizeList
height={500}
width={500}
itemCount={items.length}
itemSize={getItemSize}
>
{Row}
</VariableSizeList>
);
export default ListComponent;
Il seguente incorporamento mostra un esempio di questo componente.
La funzione di dimensione dell'elemento passata alla proprietà itemSize
randomizza le altezze delle righe
in questo esempio. In un'applicazione reale, tuttavia, dovrebbe esserci una logica effettiva
che definisce le dimensioni di ogni elemento. Idealmente, queste dimensioni devono essere calcolate in base
ai dati o ottenute da un'API.
Griglie
react-window
supporta anche la virtualizzazione di elenchi multidimensionali o griglie. In questo contesto, la "finestra" dei contenuti visibili cambia man mano che l'utente
scorrendo orizzontalmente e verticalmente.

Allo stesso modo, è possibile utilizzare i componenti FixedSizeGrid
e VariableSizeGrid
a seconda che le dimensioni di elementi di elenco specifici possano variare.
- Per
FixedSizeGrid
, l'API è pressoché la stessa, ma con la differenza che altezze, larghezze e conteggi degli elementi devono essere rappresentati sia per le colonne che per le righe. - Per
VariableSizeGrid
, è possibile modificare sia la larghezza delle colonne sia l'altezza delle righe passando le funzioni anziché i valori alle rispettive proprietà.
Dai un'occhiata alla documentazione per vedere esempi di griglie virtualizzate.
Caricamento lento durante lo scorrimento
Molti siti web migliorano le prestazioni ritardando il caricamento e il rendering degli elementi in un elenco lungo finché l'utente non ha scorri verso il basso. Questa tecnica, comunemente chiamata "caricamento infinito", aggiunge nuovi nodi DOM all'elenco man mano che l'utente scorre oltre una determinata soglia vicina alla fine. Anche se questo è meglio che caricare tutti gli elementi di un elenco contemporaneamente, il DOM viene comunque popolato con migliaia di voci di riga se l'utente ha scorre fino a quel punto. Ciò può comportare una dimensione DOM eccessivamente grande, che inizia a influire sulle prestazioni rendendo più lenti i calcoli di stile e le mutazioni DOM.
Il seguente diagramma può aiutarti a riassumere questo processo:

L'approccio migliore per risolvere questo problema è continuare a utilizzare una libreria come
react-window
per mantenere una piccola "finestra" di elementi in una pagina, ma anche
caricare in modo differito le voci più recenti man mano che l'utente scorre verso il basso. Un pacchetto separato,
react-window-infinite-loader
, lo rende possibile con react-window
.
Considera il seguente snippet di codice che mostra un esempio di stato gestito in un componente App
principale.
import React, { Component } from 'react';
import ListComponent from './ListComponent';
class App extends Component {
constructor(props) {
super(props);
this.state = {
items: [], // instantiate initial list here
moreItemsLoading: false,
hasNextPage: true
};
this.loadMore = this.loadMore.bind(this);
}
loadMore() {
// method to fetch newer entries for the list
}
render() {
const { items, moreItemsLoading, hasNextPage } = this.state;
return (
<ListComponent
items={items}
moreItemsLoading={moreItemsLoading}
loadMore={this.loadMore}
hasNextPage={hasNextPage}
/>
);
}
}
export default App;
Un metodo loadMore
viene passato a un elemento secondario ListComponent
che contiene
l'elenco del caricatore infinito. Questo è importante perché il caricatore infinito deve
attivare un callback per caricare altri elementi una volta che l'utente ha superato un determinato
punto.
Ecco come può apparire il ListComponent
che esegue il rendering dell'elenco:
import React from 'react';
import { FixedSizeList } from 'react-window';
import InfiniteLoader from "react-window-infinite-loader";
const ListComponent = ({ items, moreItemsLoading, loadMore, hasNextPage }) => {
const Row = ({ index, style }) => (
{/* define the row component using items[index] */}
);
const itemCount = hasNextPage ? items.length + 1 : items.length;
return (
<InfiniteLoader
isItemLoaded={index => index < items.length}
itemCount={itemCount}
loadMoreItems={loadMore}
>
{({ onItemsRendered, ref }) => (
<FixedSizeList
height={500}
width={500}
itemCount={itemCount}
itemSize={120}
onItemsRendered={onItemsRendered}
ref={ref}
>
{Row}
</FixedSizeList>
)}
</InfiniteLoader>
)
};
export default ListComponent;
In questo caso, il componente FixedSizeList
è racchiuso all'interno di InfiniteLoader
.
Le proprietà assegnate al caricatore sono:
isItemLoaded
: Metodo che verifica se un determinato elemento è stato caricatoitemCount
: Numero di elementi nell'elenco (o previsto)loadMoreItems
: Callback che restituisce una promessa che si risolve in dati aggiuntivi per l'elenco
Una
render prop
viene utilizzata per restituire una funzione che il componente elenco utilizza per il rendering.
Entrambi gli attributi onItemsRendered
e ref
devono essere
trasmessi.
Di seguito è riportato un esempio di come il caricamento infinito può funzionare con un elenco virtualizzato.
Scorrere l'elenco potrebbe sembrare la stessa cosa, ma ora viene effettuata una richiesta per recuperare 10 utenti da un'API utente casuale ogni volta che scorri verso la fine dell'elenco. Tutto ciò avviene visualizzando una sola "finestra" di risultati alla volta.
Controllando index
di un determinato elemento, è possibile visualizzare uno stato di caricamento diverso per un elemento a seconda che sia stata effettuata una richiesta di voci più recenti e che l'elemento sia ancora in fase di caricamento.
Ad esempio:
const Row = ({ index, style }) => {
const itemLoading = index === items.length;
if (itemLoading) {
// return loading state
} else {
// return item
}
};
Overscan
Poiché gli elementi di un elenco virtualizzato cambiano solo quando l'utente scorre, lo spazio vuoto può lampeggiare brevemente quando le voci più recenti stanno per essere visualizzate. Puoi provare a scorrere rapidamente uno degli esempi precedenti di questa guida per notarlo.
Per migliorare l'esperienza utente degli elenchi virtualizzati, react-window
ti consente di eseguire la scansione eccessiva degli elementi con la proprietà overscanCount
. In questo modo puoi
definire il numero di elementi al di fuori della "finestra" visibile da visualizzare in qualsiasi momento.
<FixedSizeList
//...
overscanCount={4}
>
{...}
</FixedSizeList>
overscanCount
funziona sia per i componenti FixedSizeList
che per quelli VariableSizeList
e ha un valore predefinito di 1. A seconda delle dimensioni di un elenco
e delle dimensioni di ogni elemento, la scansione eccessiva di più di una voce può
aiutare a evitare un lampo evidente di spazio vuoto quando l'utente scorre. Tuttavia,
la scansione eccessiva di troppe voci può influire negativamente sulle prestazioni. Lo scopo
dell'utilizzo di un elenco virtualizzato è ridurre al minimo il numero di voci a quelle
che l'utente può vedere in un determinato momento, quindi cerca di mantenere il numero di elementi
scansionati in eccesso il più basso possibile.
Per FixedSizeGrid
e VariableSizeGrid
, utilizza le proprietà overscanColumnsCount
e
overscanRowsCount
per controllare rispettivamente il numero di colonne e righe da
overscan.
Conclusione
Se non sai da dove iniziare a virtualizzare elenchi e tabelle nella tua applicazione, segui questi passaggi:
- Misura il rendimento del rendering e dello scorrimento. Questo articolo mostra come utilizzare il misuratore FPS in Chrome DevTools per esplorare l'efficienza con cui gli elementi vengono visualizzati in un elenco.
- Includi
react-window
per eventuali elenchi o griglie lunghi che influiscono sul rendimento. - Se alcune funzionalità non sono supportate in
react-window
, valuta la possibilità di utilizzarereact-virtualized
se non puoi aggiungere questa funzionalità autonomamente. - Se necessario, racchiudi l'elenco virtualizzato con
react-window-infinite-loader
per caricare gli elementi in modalità differita man mano che l'utente scorre la pagina. - Utilizza la proprietà
overscanCount
per gli elenchi e le proprietàoverscanColumnsCount
eoverscanRowsCount
per le griglie per evitare un flash di contenuti vuoti. Non eseguire la scansione di troppe voci, in quanto ciò influirà negativamente sul rendimento.