बहुत बड़ी टेबल और सूचियों से आपकी साइट की परफ़ॉर्मेंस काफ़ी धीमी हो सकती है. वर्चुअलाइज़ेशन की सुविधा से मदद मिल सकती है!
react-window
एक ऐसी लाइब्रेरी है जिसकी मदद से बड़ी सूचियों को बेहतर तरीके से रेंडर किया जा सकता है.
यहां एक सूची का उदाहरण दिया गया है, जिसमें 1,000 पंक्तियां हैं और इन्हें react-window
के साथ रेंडर किया जा रहा है. जितनी तेज़ हो सके उतनी तेज़ी से स्क्रीन पर नीचे की ओर स्क्रोल करें.
यह जानकारी आपके काम की क्यों है?
ऐसा हो सकता है कि आपको ऐसी बड़ी टेबल या सूची दिखानी पड़े जिसमें कई पंक्तियां हों. ऐसी सूची में हर आइटम को लोड करने से, परफ़ॉर्मेंस पर काफ़ी असर पड़ सकता है.
सूची को वर्चुअलाइज़ करना या "विंडो करना", सिर्फ़ उस डेटा को रेंडर करने का कॉन्सेप्ट है जो उपयोगकर्ता को दिखता है. शुरुआत में रेंडर किए जाने वाले एलिमेंट की संख्या, पूरी सूची का बहुत छोटा सबसेट होता है. साथ ही, जब उपयोगकर्ता स्क्रोल करता रहता है, तो दिखने वाले कॉन्टेंट की "विंडो" बदले जाती है. इससे सूची की रेंडरिंग और स्क्रोल करने की परफ़ॉर्मेंस, दोनों बेहतर होती हैं.
"विंडो" से बाहर निकलने वाले डीओएम नोड को रीसाइकल किया जाता है या उपयोगकर्ता सूची को नीचे की ओर स्क्रोल करते ही, उन्हें नए एलिमेंट से तुरंत बदल दिया जाता है. इससे, विंडो के साइज़ के हिसाब से रेंडर किए गए सभी एलिमेंट की संख्या बनी रहती है.
react-window
react-window
, तीसरे पक्ष की एक छोटी लाइब्रेरी है. इससे आपके ऐप्लिकेशन में वर्चुअलाइज़ की गई सूचियां बनाना आसान हो जाता है. यह कई बुनियादी एपीआई उपलब्ध कराता है, जिनका इस्तेमाल अलग-अलग तरह की सूचियों और टेबल के लिए किया जा सकता है.
तय साइज़ की सूचियों का इस्तेमाल कब करना चाहिए
अगर आपके पास एक जैसे साइज़ के आइटम की लंबी और एक डाइमेंशन वाली सूची है, तो FixedSizeList
कॉम्पोनेंट का इस्तेमाल करें.
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;
FixedSizeList
कॉम्पोनेंट मेंheight
,width
, औरitemSize
प्रॉपर्टी का इस्तेमाल किया जा सकता है. इससे, सूची में मौजूद आइटम का साइज़ कंट्रोल किया जा सकता है.- लाइनों को रेंडर करने वाला फ़ंक्शन,
FixedSizeList
को चाइल्ड के तौर पर पास किया जाता है. किसी आइटम की जानकारी,index
आर्ग्युमेंट (items[index]
) की मदद से ऐक्सेस की जा सकती है. style
पैरामीटर को लाइन रेंडर करने के उस तरीके में भी पास किया जाता है जिसे लाइन एलिमेंट से अटैच करना ज़रूरी है. सूची के आइटम को पूरी तरह से तय की गई जगह पर दिखाया जाता है. इसके लिए, इनलाइन में उनकी ऊंचाई और चौड़ाई की वैल्यू असाइन की जाती है. इसकी ज़िम्मेदारीstyle
पैरामीटर की होती है.
इस लेख में पहले दिखाया गया Glitch का उदाहरण, FixedSizeList
कॉम्पोनेंट का उदाहरण है.
अलग-अलग साइज़ की सूचियों का इस्तेमाल कब करना चाहिए
अलग-अलग साइज़ वाले आइटम की सूची को रेंडर करने के लिए, VariableSizeList
कॉम्पोनेंट का इस्तेमाल करें. यह कॉम्पोनेंट, तय साइज़ की सूची की तरह ही काम करता है. हालांकि, इसमें किसी खास वैल्यू के बजाय itemSize
प्रॉपर्टी के लिए फ़ंक्शन की ज़रूरत होती है.
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;
यहां दिए गए एम्बेड में, इस कॉम्पोनेंट का उदाहरण दिखाया गया है.
इस उदाहरण में, itemSize
प्रॉपर्टी में पास किया गया आइटम साइज़ फ़ंक्शन, पंक्ति की ऊंचाई को रैंडम तौर पर तय करता है. हालांकि, किसी असल ऐप्लिकेशन में, हर आइटम के साइज़ तय करने के लिए कोई लॉजिक होना चाहिए. आम तौर पर, इन साइज़ का हिसाब डेटा के आधार पर लगाया जाना चाहिए या एपीआई से पाया जाना चाहिए.
ग्रिड
react-window
, कई डाइमेंशन वाली सूचियों या ग्रिड को वर्चुअलाइज़ करने की सुविधा भी देता है. इस संदर्भ में, जब उपयोगकर्ता हॉरिज़ॉन्टल और वर्टिकल तौर पर स्क्रोल करता है, तो दिखने वाले कॉन्टेंट की "विंडो" बदल जाती है.
इसी तरह, FixedSizeGrid
और VariableSizeGrid
, दोनों कॉम्पोनेंट का इस्तेमाल किया जा सकता है. यह इस बात पर निर्भर करता है कि सूची के किसी आइटम का साइज़ अलग-अलग हो सकता है या नहीं.
FixedSizeGrid
के लिए, एपीआई लगभग एक जैसा है. हालांकि, कॉलम और पंक्तियों, दोनों के लिए ऊंचाई, चौड़ाई, और आइटम की संख्या दिखानी होगी.VariableSizeGrid
के लिए, कॉलम की चौड़ाई और पंक्ति की ऊंचाई, दोनों को बदला जा सकता है. इसके लिए, उनके प्रॉप में वैल्यू के बजाय फ़ंक्शन डालें.
वर्चुअलाइज़ किए गए ग्रिड के उदाहरण देखने के लिए, दस्तावेज़ देखें.
स्क्रोल करने पर लेज़ी लोडिंग
कई वेबसाइटें, लंबी सूची में मौजूद आइटम को तब तक लोड और रेंडर नहीं करतीं, जब तक उपयोगकर्ता नीचे की ओर स्क्रोल नहीं कर लेता. इससे वेबसाइट की परफ़ॉर्मेंस बेहतर होती है. इस तकनीक को आम तौर पर "इनफ़ाइनाइट लोडिंग" कहा जाता है. इसमें सूची में नए DOM नोड तब जोड़े जाते हैं, जब उपयोगकर्ता पेज के आखिर में मौजूद किसी तय थ्रेशोल्ड तक स्क्रोल करता है. हालांकि, यह किसी सूची में सभी आइटम को एक साथ लोड करने से बेहतर है, लेकिन अगर उपयोगकर्ता ने उससे ज़्यादा स्क्रोल किया है, तो डीओएम में हज़ारों पंक्ति की एंट्री अपने-आप भर जाती हैं. इससे DOM का साइज़ बहुत ज़्यादा हो सकता है. इससे स्टाइल कैलकुलेशन और DOM म्यूटेशन धीमे होने पर, परफ़ॉर्मेंस पर असर पड़ता है.
इस डायग्राम से, इस बारे में खास जानकारी मिल सकती है:
इस समस्या को हल करने का सबसे अच्छा तरीका यह है कि react-window
जैसी लाइब्रेरी का इस्तेमाल जारी रखा जाए, ताकि पेज पर एलिमेंट की छोटी "विंडो" बनी रहे. साथ ही, उपयोगकर्ता के नीचे की ओर स्क्रोल करने पर, नई एंट्री को भी धीरे-धीरे लोड किया जा सके. react-window-infinite-loader
नाम का एक अलग पैकेज, react-window
के साथ ऐसा करने की सुविधा देता है.
नीचे दिए गए कोड पर ध्यान दें. इसमें, पैरंट App
कॉम्पोनेंट में मैनेज की जाने वाली स्थिति का उदाहरण दिया गया है.
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;
loadMore
तरीके को चाइल्ड ListComponent
में पास किया जाता है, जिसमें अनलिमिटेड लोडर की सूची होती है. यह ज़रूरी है, क्योंकि जब उपयोगकर्ता किसी तय बिंदु से आगे स्क्रोल करता है, तो इनफ़ाइनाइट लोडर को ज़्यादा आइटम लोड करने के लिए, कॉलबैक ट्रिगर करना होता है.
सूची को रेंडर करने वाला ListComponent
इस तरह दिख सकता है:
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;
यहां, FixedSizeList
कॉम्पोनेंट को InfiniteLoader
के अंदर रैप किया गया है.
लोडर के लिए असाइन किए गए प्रॉप ये हैं:
isItemLoaded
: यह तरीका यह जांचता है कि कोई आइटम लोड हुआ है या नहींitemCount
: सूची में मौजूद आइटम की संख्या (या उम्मीद के मुताबिक)loadMoreItems
: ऐसा कॉलबैक जो सूची के लिए अतिरिक्त डेटा दिखाने वाला प्रॉमिस दिखाता है
रेंडर प्रॉप का इस्तेमाल, एक ऐसा फ़ंक्शन दिखाने के लिए किया जाता है जिसका इस्तेमाल सूची कॉम्पोनेंट, रेंडर करने के लिए करता है.
onItemsRendered
और ref
, दोनों एट्रिब्यूट ऐसे एट्रिब्यूट हैं जिन्हें सबमिट करना ज़रूरी है.
यहां एक उदाहरण दिया गया है, जिसमें दिखाया गया है कि वर्चुअलाइज़ की गई सूची के साथ अनलिमिटेड लोडिंग कैसे काम कर सकती है.
सूची को नीचे की ओर स्क्रोल करने पर, आपको पहले जैसा ही अनुभव मिल सकता है. हालांकि, अब सूची के आखिर तक स्क्रोल करने पर, रैंडम उपयोगकर्ता एपीआई से 10 उपयोगकर्ताओं को वापस पाने का अनुरोध किया जाता है. यह सब तब किया जाता है, जब एक बार में सिर्फ़ नतीजों की एक "विंडो" रेंडर की जाती है.
किसी आइटम के index
की जांच करके, उस आइटम के लिए लोड होने की अलग-अलग स्थिति देखी जा सकती है. यह इस बात पर निर्भर करता है कि नई एंट्री के लिए अनुरोध किया गया है या नहीं और आइटम अब भी लोड हो रहा है या नहीं.
उदाहरण के लिए:
const Row = ({ index, style }) => {
const itemLoading = index === items.length;
if (itemLoading) {
// return loading state
} else {
// return item
}
};
ओवरस्कैनिंग
वर्चुअलाइज़ की गई सूची में मौजूद आइटम सिर्फ़ तब बदलते हैं, जब उपयोगकर्ता स्क्रोल करता है. इसलिए, नई एंट्री दिखने वाली हैं, तो खाली जगह कुछ समय के लिए फ़्लैश हो सकती है. इस बात को समझने के लिए, इस गाइड में दिए गए किसी भी उदाहरण को तेज़ी से स्क्रोल करें.
वर्चुअलाइज़ की गई सूचियों के उपयोगकर्ता अनुभव को बेहतर बनाने के लिए, react-window
की मदद से overscanCount
प्रॉपर्टी के साथ आइटम को ओवरस्कैन किया जा सकता है. इससे, यह तय किया जा सकता है कि दिखने वाली "विंडो" के बाहर, कितने आइटम हमेशा रेंडर किए जाएं.
<FixedSizeList
//...
overscanCount={4}
>
{...}
</FixedSizeList>
overscanCount
, FixedSizeList
और VariableSizeList
दोनों कॉम्पोनेंट के लिए काम करता है. इसकी डिफ़ॉल्ट वैल्यू 1 होती है. सूची के साइज़ और हर आइटम के साइज़ के आधार पर, एक से ज़्यादा एंट्री को ओवरस्कैन करने से, उपयोगकर्ता के स्क्रोल करने पर खाली जगह के फ़्लैश होने से रोका जा सकता है. हालांकि, बहुत ज़्यादा एंट्री को स्कैन करने से परफ़ॉर्मेंस पर बुरा असर पड़ सकता है. वर्चुअलाइज़ की गई सूची का इस्तेमाल करने का मकसद, उन एंट्री की संख्या को कम करना है जिन्हें उपयोगकर्ता किसी भी समय देख सकता है. इसलिए, ज़रूरत से ज़्यादा स्कैन किए गए आइटम की संख्या को कम से कम रखें.
FixedSizeGrid
और VariableSizeGrid
के लिए, overscanColumnsCount
और
overscanRowsCount
प्रॉपर्टी का इस्तेमाल करके, क्रमशः ओवरस्कैन करने के लिए कॉलम और पंक्तियों की संख्या को कंट्रोल करें.
नतीजा
अगर आपको यह नहीं पता कि अपने ऐप्लिकेशन में सूचियों और टेबल को वर्चुअलाइज़ करने की प्रोसेस कहां से शुरू करनी है, तो यह तरीका अपनाएं:
- रेंडरिंग और स्क्रोलिंग की परफ़ॉर्मेंस मेज़र करें. इस लेख में बताया गया है कि Chrome DevTools में एफ़पीएस (फ़्रेम प्रति सेकंड) मेटर का इस्तेमाल करके, यह कैसे पता लगाया जा सकता है कि किसी सूची में आइटम कितनी तेज़ी से रेंडर होते हैं.
- परफ़ॉर्मेंस पर असर डालने वाली लंबी सूचियों या ग्रिड के लिए
react-window
शामिल करें. - अगर
react-window
में कुछ सुविधाएं काम नहीं करती हैं, तोreact-virtualized
का इस्तेमाल करें. हालांकि, अगर आपके पास खुद से यह सुविधा जोड़ने का विकल्प नहीं है, तो ऐसा करें. - अगर आपको उपयोगकर्ता के स्क्रोल करने पर, आइटम को धीरे-धीरे लोड करना है, तो अपनी वर्चुअलाइज़ की गई सूची को
react-window-infinite-loader
के साथ रैप करें. - खाली कॉन्टेंट को फ़्लैश होने से रोकने के लिए, अपनी सूचियों के लिए
overscanCount
प्रॉपर्टी और अपने ग्रिड के लिएoverscanColumnsCount
औरoverscanRowsCount
प्रॉपर्टी का इस्तेमाल करें. बहुत ज़्यादा एंट्री को ओवरस्कैन न करें, क्योंकि इससे परफ़ॉर्मेंस पर बुरा असर पड़ेगा.