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 कॉम्पोनेंट के साथ जोड़ते समय, लोड होने की स्थितियों पर ध्यान दिया जा सकता है.

Suspense

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

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

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

लोड होने के इस प्रकार की गड़बड़ियों को अच्छी तरह से ठीक करने के लिए, 'रिऐक्ट' का एक स्टैंडर्ड पैटर्न है: गड़बड़ी की सीमा का इस्तेमाल करना. जैसा कि दस्तावेज़ में बताया गया है, अगर प्रतिक्रिया देने वाला कोई भी कॉम्पोनेंट, लाइफ़साइकल के तरीकों 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-router के साथ Suspense को कैसे इस्तेमाल किया जा सकता है.
  2. अपनी साइट के किसी पेज पर ऐसे बड़े कॉम्पोनेंट की पहचान करें जो सिर्फ़ उपयोगकर्ता के कुछ इंटरैक्शन (जैसे, किसी बटन पर क्लिक करना) पर रेंडर होते हैं. इन कॉम्पोनेंट को अलग-अलग करने से, आपके JavaScript पेलोड कम हो जाएंगे.
  3. हर ऐसी चीज़ को अलग-अलग हिस्सों में बांट दें जो उपयोगकर्ता के लिए ज़रूरी न हो और जिसकी वजह से स्क्रीन पर जगह न दिखती हो.