बहुत बड़ी टेबल और सूचियां आपकी साइट की परफ़ॉर्मेंस को काफ़ी धीमा कर सकती हैं. वर्चुअलाइज़ेशन की सुविधा से मदद मिल सकती है!
react-window
एक ऐसी लाइब्रेरी है जिसकी मदद से बड़ी सूचियों को बेहतर तरीके से रेंडर किया जा सकता है.
यहां एक सूची का उदाहरण दिया गया है, जिसमें react-window
के साथ रेंडर की जा रही 1,000 पंक्तियां हैं. जितनी तेज़ हो सके उतनी तेज़ी से स्क्रीन पर नीचे की ओर स्क्रोल करें.
यह जानकारी आपके काम की क्यों है?
कभी-कभी ऐसा भी हो सकता है कि आपको एक बड़ी टेबल या सूची दिखानी पड़े जिसमें कई पंक्तियां हों. ऐसी सूची में हर आइटम को लोड करने से, परफ़ॉर्मेंस पर काफ़ी असर पड़ सकता है.
सूची को वर्चुअलाइज़ करना या "विंडो करना", सिर्फ़ उस डेटा को रेंडर करने का कॉन्सेप्ट है जो उपयोगकर्ता को दिखता है. शुरुआत में रेंडर किए जाने वाले एलिमेंट की संख्या, पूरी सूची का बहुत छोटा सबसेट होता है. साथ ही, जब उपयोगकर्ता स्क्रोल करता रहता है, तो दिखने वाले कॉन्टेंट की "विंडो" बदले जाती है. इससे सूची को रेंडर करने और स्क्रोल करने की परफ़ॉर्मेंस, दोनों बेहतर होती हैं.
"विंडो" से बाहर निकलने वाले DOM नोड रीसाइकल किए जाते हैं या जैसे ही उपयोगकर्ता सूची में नीचे की ओर स्क्रोल करता है, तो उन्हें नए एलिमेंट से बदल दिया जाता है. इससे, विंडो के साइज़ के हिसाब से रेंडर किए गए सभी एलिमेंट की संख्या तय रहती है.
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
प्रॉपर्टी का इस्तेमाल करें. बहुत ज़्यादा एंट्री को ओवरस्कैन न करें, क्योंकि इससे परफ़ॉर्मेंस पर बुरा असर पड़ेगा.