Tabel dan daftar yang sangat besar dapat memperlambat performa situs Anda secara signifikan. Virtualisasi dapat membantu.
react-window
adalah
library yang memungkinkan daftar besar dirender secara efisien.
Berikut adalah contoh daftar yang berisi 1.000 baris yang dirender dengan
react-window
. Coba scroll secepat mungkin.
Mengapa hal ini bermanfaat?
Terkadang Anda perlu menampilkan tabel atau daftar besar yang berisi banyak baris. Memuat setiap item dalam daftar tersebut dapat memengaruhi performa secara signifikan.
Virtualisasi daftar, atau "windowing", adalah konsep yang hanya merender apa yang terlihat oleh pengguna. Jumlah elemen yang dirender pada awalnya adalah subkumpulan yang sangat kecil dari seluruh daftar dan "jendela" konten yang terlihat bergerak saat pengguna terus men-scroll. Hal ini meningkatkan performa rendering dan scroll daftar.
Node DOM yang keluar dari "jendela" akan didaur ulang, atau segera diganti dengan elemen yang lebih baru saat pengguna men-scroll daftar ke bawah. Hal ini akan menjaga jumlah semua elemen yang dirender khusus untuk ukuran jendela.
react-window
react-window
adalah library pihak ketiga kecil yang memudahkan
pembuatan daftar virtual di aplikasi Anda. Library ini menyediakan sejumlah API dasar
yang dapat digunakan untuk berbagai jenis daftar dan tabel.
Kapan harus menggunakan daftar berukuran tetap
Gunakan komponen FixedSizeList
jika Anda memiliki daftar satu dimensi yang panjang
dari item berukuran sama.
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;
- Komponen
FixedSizeList
menerima propertiheight
,width
, danitemSize
untuk mengontrol ukuran item dalam daftar. - Fungsi yang merender baris diteruskan sebagai turunan ke
FixedSizeList
. Detail tentang item tertentu dapat diakses dengan argumenindex
(items[index]
). - Parameter
style
juga diteruskan ke metode rendering baris yang harus dilampirkan ke elemen baris. Item daftar diposisikan secara absolut dengan nilai tinggi dan lebarnya ditetapkan secara inline, dan parameterstyle
bertanggung jawab untuk hal ini.
Contoh Glitch yang ditampilkan sebelumnya dalam artikel ini menunjukkan contoh
komponen FixedSizeList
.
Kapan harus menggunakan daftar berukuran variabel
Gunakan komponen VariableSizeList
untuk merender daftar item yang memiliki
ukuran berbeda. Komponen ini berfungsi dengan cara yang sama seperti daftar ukuran tetap, tetapi
mengharapkan fungsi untuk properti itemSize
, bukan nilai tertentu.
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;
Penyematan berikut menunjukkan contoh komponen ini.
Fungsi ukuran item yang diteruskan ke properti itemSize
akan mengacak tinggi baris
dalam contoh ini. Namun, dalam aplikasi yang sebenarnya, harus ada logika sebenarnya
yang menentukan ukuran setiap item. Idealnya, ukuran ini harus dihitung berdasarkan
data atau diperoleh dari API.
Petak
react-window
juga menyediakan dukungan untuk memvirtualisasi daftar atau grid multi-dimensi. Dalam konteks ini, "jendela" konten yang terlihat akan berubah saat pengguna
men-scroll secara horizontal dan vertikal.
Demikian pula, komponen FixedSizeGrid
dan VariableSizeGrid
dapat digunakan
bergantung pada apakah ukuran item daftar tertentu dapat bervariasi.
- Untuk
FixedSizeGrid
, API-nya hampir sama, tetapi dengan fakta bahwa tinggi, lebar, dan jumlah item harus direpresentasikan untuk kolom dan baris. - Untuk
VariableSizeGrid
, lebar kolom dan tinggi baris dapat diubah dengan meneruskan fungsi, bukan nilai, ke properti masing-masing.
Lihat dokumentasi untuk melihat contoh petak virtual.
Pemuatan lambat saat men-scroll
Banyak situs meningkatkan performa dengan menunggu pemuatan dan rendering item dalam daftar yang panjang hingga pengguna men-scroll ke bawah. Teknik ini, yang biasa disebut sebagai "pemuatan tanpa batas", menambahkan node DOM baru ke dalam daftar saat pengguna men-scroll melewati nilai minimum tertentu mendekati akhir. Meskipun lebih baik daripada memuat semua item dalam daftar sekaligus, tindakan ini tetap akan mengisi DOM dengan ribuan entri baris jika pengguna telah men-scroll melewati jumlah tersebut. Hal ini dapat menyebabkan ukuran DOM yang terlalu besar, yang mulai memengaruhi performa dengan membuat penghitungan gaya dan mutasi DOM menjadi lebih lambat.
Diagram berikut mungkin membantu meringkas hal ini:
Pendekatan terbaik untuk mengatasi masalah ini adalah terus menggunakan library seperti
react-window
untuk mempertahankan "jendela" elemen kecil di halaman, tetapi juga
memuat lambat entri yang lebih baru saat pengguna men-scroll ke bawah. Paket terpisah,
react-window-infinite-loader
, memungkinkan hal ini dengan react-window
.
Pertimbangkan potongan kode berikut yang menunjukkan contoh status yang
dikelola di komponen App
induk.
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;
Metode loadMore
diteruskan ke ListComponent
turunan yang berisi
daftar loader tanpa batas. Hal ini penting karena loader tanpa batas perlu
memicu callback untuk memuat lebih banyak item setelah pengguna men-scroll melewati titik
tertentu.
Berikut tampilan ListComponent
yang merender daftar:
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;
Di sini, komponen FixedSizeList
digabungkan dalam InfiniteLoader
.
Properti yang ditetapkan ke loader adalah:
isItemLoaded
: Metode yang memeriksa apakah item tertentu telah dimuatitemCount
: Jumlah item dalam daftar (atau yang diharapkan)loadMoreItems
: Callback yang menampilkan promise yang me-resolve ke data tambahan untuk daftar
Prop render digunakan untuk menampilkan fungsi yang digunakan komponen daftar untuk merender.
Atribut onItemsRendered
dan ref
adalah atribut yang perlu
diteruskan.
Berikut adalah contoh cara kerja pemuatan tanpa batas dengan daftar virtual.
Men-scroll daftar ke bawah mungkin terasa sama, tetapi permintaan kini dibuat untuk mengambil 10 pengguna dari API pengguna acak setiap kali Anda men-scroll mendekati akhir daftar. Semua ini dilakukan hanya dengan merender satu "jendela" hasil dalam satu waktu.
Dengan memeriksa index
item tertentu, status pemuatan yang berbeda dapat
ditampilkan untuk item, bergantung pada apakah permintaan telah dibuat untuk entri yang lebih baru
dan item masih dimuat.
Contoh:
const Row = ({ index, style }) => {
const itemLoading = index === items.length;
if (itemLoading) {
// return loading state
} else {
// return item
}
};
Overscan
Karena item dalam daftar virtual hanya berubah saat pengguna men-scroll, ruang kosong dapat berkedip sebentar saat entri yang lebih baru akan ditampilkan. Anda dapat mencoba men-scroll dengan cepat contoh sebelumnya dalam panduan ini untuk melihat hal ini.
Untuk meningkatkan pengalaman pengguna daftar virtual, react-window
memungkinkan
Anda melakukan pemindaian berlebih pada item dengan properti overscanCount
. Hal ini memungkinkan Anda
menentukan jumlah item di luar "jendela" yang terlihat untuk dirender setiap saat.
<FixedSizeList
//...
overscanCount={4}
>
{...}
</FixedSizeList>
overscanCount
berfungsi untuk komponen FixedSizeList
dan VariableSizeList
dan memiliki nilai default 1. Bergantung pada ukuran daftar
serta ukuran setiap item, pemindaian berlebihan lebih dari satu entri dapat
membantu mencegah flash ruang kosong yang terlihat saat pengguna men-scroll. Namun,
memindai terlalu banyak entri dapat memengaruhi performa secara negatif. Tujuan
utama penggunaan daftar virtual adalah untuk meminimalkan jumlah entri yang dapat
dilihat pengguna pada waktu tertentu, jadi cobalah untuk menjaga jumlah item yang terlalu banyak
dipindai serendah mungkin.
Untuk FixedSizeGrid
dan VariableSizeGrid
, gunakan properti overscanColumnsCount
dan
overscanRowsCount
untuk mengontrol jumlah kolom dan baris yang akan
di-overscan.
Kesimpulan
Jika Anda tidak yakin harus memulai virtualisasi daftar dan tabel di aplikasi, ikuti langkah-langkah berikut:
- Ukur performa rendering dan scroll. Artikel ini menunjukkan cara pengukur FPS di Chrome DevTools dapat digunakan untuk mempelajari seberapa efisien item dirender dalam daftar.
- Sertakan
react-window
untuk daftar atau petak panjang yang memengaruhi performa. - Jika ada fitur tertentu yang tidak didukung di
react-window
, pertimbangkan untuk menggunakanreact-virtualized
jika Anda tidak dapat menambahkan fungsi ini sendiri. - Gabungkan daftar virtual Anda dengan
react-window-infinite-loader
jika Anda perlu memuat item secara lambat saat pengguna men-scroll. - Gunakan properti
overscanCount
untuk daftar dan propertioverscanColumnsCount
danoverscanRowsCount
untuk petak untuk mencegah flash konten kosong. Jangan melakukan pemindaian berlebihan pada terlalu banyak entri karena hal ini akan berdampak negatif pada performa.