React.lazy और Suspense के साथ कोड बांटना

आपको अपने उपयोगकर्ताओं को ज़रूरत से ज़्यादा कोड कभी भी शिप करने की ज़रूरत नहीं होती. इसलिए, अपने बंडल को अलग-अलग करके पक्का करें कि ऐसा कभी न हो!

React.lazy तरीके से, डाइनैमिक इंपोर्ट का इस्तेमाल करके, कॉम्पोनेंट लेवल पर React ऐप्लिकेशन को आसानी से कोड-स्प्लिट किया जा सकता है.

import React, { lazy } from 'react';

const AvatarComponent = lazy(() => import('./AvatarComponent'));

const DetailsComponent = () => (
  <div>
    <AvatarComponent />
  </div>
)

यह जानकारी काम की क्यों है?

आम तौर पर, बड़े React ऐप्लिकेशन में कई कॉम्पोनेंट, यूटिलिटी के तरीके, और तीसरे पक्ष की लाइब्रेरी शामिल होती हैं. अगर किसी ऐप्लिकेशन के अलग-अलग हिस्सों को सिर्फ़ ज़रूरत पड़ने पर लोड करने की कोशिश नहीं की जाती है, तो उपयोगकर्ताओं के पहले पेज को लोड करने के साथ ही, उन्हें JavaScript का एक बड़ा बंडल भेज दिया जाएगा. इससे पेज की परफ़ॉर्मेंस पर काफ़ी असर पड़ सकता है.

React.lazy फ़ंक्शन, ऐप्लिकेशन के कॉम्पोनेंट को JavaScript के अलग-अलग हिस्सों में बांटने का एक आसान तरीका उपलब्ध कराता है. इसके बाद, Suspense कॉम्पोनेंट के साथ जोड़कर, लोडिंग स्टेटस को मैनेज किया जा सकता है.

सस्पेंस

उपयोगकर्ताओं को एक बड़ा JavaScript पेलोड भेजने में समस्या यह है कि पेज को पूरी तरह लोड होने में लगने वाला समय, खास तौर पर कमज़ोर डिवाइसों और नेटवर्क कनेक्शन पर होता है. इसलिए, कोड को बांटना और लेज़ी लोडिंग बहुत ज़्यादा कारगर होती है.

हालांकि, नेटवर्क पर कोड-स्प्लिट कॉम्पोनेंट फ़ेच किए जाने पर, उपयोगकर्ताओं को थोड़ा समय लग सकता है. इसलिए, कॉन्टेंट लोड होने की सही स्थिति दिखाना ज़रूरी है. Suspense घटक के साथ React.lazy का इस्तेमाल करने से, इस समस्या को हल करने में मदद मिलती है.

import React, { lazy, Suspense } from 'react';

const AvatarComponent = lazy(() => import('./AvatarComponent'));

const renderLoader = () => <p>Loading</p>;

const DetailsComponent = () => (
  <Suspense fallback={renderLoader()}>
    <AvatarComponent />
  </Suspense>
)

Suspense, fallback कॉम्पोनेंट को स्वीकार करता है. इसकी मदद से, किसी भी React कॉम्पोनेंट को लोड होने की स्थिति के तौर पर दिखाया जा सकता है. नीचे दिए गए उदाहरण में, यह दिखाया गया है कि यह सुविधा कैसे काम करती है. अवतार सिर्फ़ तब रेंडर किया जाता है, जब बटन पर क्लिक किया जाता है. इसके बाद, निलंबित AvatarComponent के लिए ज़रूरी कोड को वापस पाने का अनुरोध किया जाता है. इस दौरान, फ़ॉलबैक लोडिंग कॉम्पोनेंट दिखाया जाता है.

यहां, AvatarComponent बनाने वाला कोड छोटा है. इसलिए, लोडिंग स्पिनर सिर्फ़ कुछ समय के लिए दिखता है. बड़े कॉम्पोनेंट को लोड होने में ज़्यादा समय लग सकता है. खास तौर पर, खराब नेटवर्क कनेक्शन पर.

इस सुविधा के काम करने का तरीका बेहतर तरीके से समझने के लिए:

  • साइट की झलक देखने के लिए, ऐप्लिकेशन देखें दबाएं. इसके बाद, फ़ुलस्क्रीनफ़ुलस्क्रीन दबाएं.
  • DevTools खोलने के लिए, `Control+Shift+J` दबाएं. Mac पर, `Command+Option+J` दबाएं.
  • नेटवर्क टैब पर क्लिक करें.
  • थ्रॉटलिंग ड्रॉपडाउन पर क्लिक करें. यह डिफ़ॉल्ट रूप से थ्रॉटलिंग नहीं पर सेट होता है. फ़ास्ट 3G चुनें.
  • ऐप्लिकेशन में मुझ पर क्लिक करें बटन पर क्लिक करें.

लोड होने की जानकारी देने वाला इंडिकेटर अब ज़्यादा समय तक दिखेगा. ध्यान दें कि AvatarComponent को बनाने वाले सभी कोड को अलग-अलग चंक के तौर पर कैसे फ़ेच किया जाता है.

DevTools का नेटवर्क पैनल, जिसमें एक chunk.js फ़ाइल डाउनलोड की जा रही है

एक से ज़्यादा कॉम्पोनेंट को निलंबित करना

Suspense की एक और सुविधा यह है कि इसकी मदद से, एक से ज़्यादा कॉम्पोनेंट को लोड होने से रोका जा सकता है. भले ही, वे सभी कॉम्पोनेंट लेज़ी लोड किए गए हों.

उदाहरण के लिए:

import React, { lazy, Suspense } from 'react';

const AvatarComponent = lazy(() => import('./AvatarComponent'));
const InfoComponent = lazy(() => import('./InfoComponent'));
const MoreInfoComponent = lazy(() => import('./MoreInfoComponent'));

const renderLoader = () => <p>Loading</p>;

const DetailsComponent = () => (
  <Suspense fallback={renderLoader()}>
    <AvatarComponent />
    <InfoComponent />
    <MoreInfoComponent />
  </Suspense>
)

यह एक बहुत ही काम का तरीका है. इससे, लोड होने की सिर्फ़ एक स्थिति दिखाते हुए, कई कॉम्पोनेंट के रेंडर होने में देरी की जा सकती है. सभी कॉम्पोनेंट फ़ेच हो जाने के बाद, उपयोगकर्ता को एक साथ सभी कॉम्पोनेंट दिखते हैं.

इसे यहां दिए गए एम्बेड की मदद से देखा जा सकता है:

इसके बिना, अलग-अलग हिस्सों में लोड होने या यूज़र इंटरफ़ेस (यूआई) के अलग-अलग हिस्सों के एक-एक करके लोड होने की समस्या आ सकती है. साथ ही, हर हिस्से के लिए लोड होने का अलग-अलग इंडिकेटर भी दिख सकता है. इससे, उपयोगकर्ता अनुभव खराब हो सकता है.

लोड न होने की समस्याएं हल करना

Suspense की मदद से, नेटवर्क के अनुरोधों के दौरान, लोड होने की अस्थायी स्थिति दिखाई जा सकती है. लेकिन, अगर किसी वजह से नेटवर्क के अनुरोध पूरा नहीं हो पाते हैं, तो क्या होगा? ऐसा हो सकता है कि आप ऑफ़लाइन हों या आपका वेब ऐप्लिकेशन, वर्शन वाले यूआरएल को धीरे-धीरे लोड करने की कोशिश कर रहा हो. यह यूआरएल पुराना हो सकता है और सर्वर को फिर से डिप्लॉय करने के बाद, अब उपलब्ध न हो.

React में, लोड होने में होने वाली इस तरह की गड़बड़ियों को ठीक करने के लिए एक स्टैंडर्ड पैटर्न है: गड़बड़ी की सीमा का इस्तेमाल करना. दस्तावेज़ में बताए गए तरीके के मुताबिक, अगर कोई React कॉम्पोनेंट लाइफ़साइकल के तरीकों static getDerivedStateFromError() या componentDidCatch() में से किसी एक या दोनों को लागू करता है, तो वह गड़बड़ी की सीमा के तौर पर काम कर सकता है.

लैज़ी लोडिंग के काम न करने की समस्या का पता लगाने और उसे मैनेज करने के लिए, अपने Suspense कॉम्पोनेंट को ऐसे पैरंट कॉम्पोनेंट के साथ रैप करें जो गड़बड़ी की सीमा के तौर पर काम करता है. गड़बड़ी की सीमा वाले render() तरीके के अंदर, चाइल्ड एंट्री को ऐसे ही दिखाया जा सकता है जैसे कि कोई गड़बड़ी न हो. इसके अलावा, अगर कोई गड़बड़ी होती है, तो कस्टम गड़बड़ी का मैसेज दिखाया जा सकता है:

import React, { lazy, Suspense } from 'react';

const AvatarComponent = lazy(() => import('./AvatarComponent'));
const InfoComponent = lazy(() => import('./InfoComponent'));
const MoreInfoComponent = lazy(() => import('./MoreInfoComponent'));

const renderLoader = () => <p>Loading</p>;

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = {hasError: false};
  }

  static getDerivedStateFromError(error) {
    return {hasError: true};
  }

  render() {
    if (this.state.hasError) {
      return <p>Loading failed! Please reload.</p>;
    }

    return this.props.children;
  }
}

const DetailsComponent = () => (
  <ErrorBoundary>
    <Suspense fallback={renderLoader()}>
      <AvatarComponent />
      <InfoComponent />
      <MoreInfoComponent />
    </Suspense>
  </ErrorBoundary>
)

नतीजा

अगर आपको नहीं पता कि अपने React ऐप्लिकेशन में कोड स्प्लिटिंग को कहां से शुरू करना है, तो यह तरीका अपनाएं:

  1. रूट लेवल से शुरू करें. मार्ग आपके ऐप्लिकेशन के उन बिंदुओं की पहचान करने का सबसे आसान तरीका हैं जिन्हें विभाजित किया जा सकता है. React दस्तावेज़ में दिखाया गया है कि Suspense का इस्तेमाल, react-router के साथ कैसे किया जा सकता है.
  2. अपनी साइट के किसी पेज पर ऐसे बड़े कॉम्पोनेंट की पहचान करें जो सिर्फ़ उपयोगकर्ता के कुछ इंटरैक्शन (जैसे, बटन पर क्लिक करना) पर रेंडर होते हैं. इन कॉम्पोनेंट को अलग-अलग करने से, आपके JavaScript पेलोड कम हो जाएंगे.
  3. ऑफ़स्क्रीन और उपयोगकर्ता के लिए ज़रूरी नहीं होने वाली किसी भी चीज़ को अलग से लोड करें.