वेब ऐप्लिकेशन को तेज़ी से लोड करने की तकनीक, यहां तक कि फ़ीचर फ़ोन पर भी लोड होती है

हमने PROXX में कोड स्प्लिटिंग, कोड इनलाइनिंग, और सर्वर-साइड रेंडरिंग का इस्तेमाल कैसे किया.

Google I/O 2019 मैरिको, जेक और मैंने PROXX शिप किया. यह वेब के लिए एक आधुनिक माइनस्वीपर-क्लोन है. ऐसी सुविधा जो PROXX को सबसे अलग बनाती है वह है सुलभता (इसे स्क्रीन रीडर की मदद से चलाया जा सकता है!) और किसी महंगे डेस्कटॉप डिवाइस की तरह फ़ीचर फ़ोन पर भी इसे चलाया जा सकता है. फ़ीचर फ़ोन को कई तरीकों से सीमित किया जाता है:

  • कमज़ोर सीपीयू
  • कमज़ोर या मौजूद नहीं जीपीयू
  • टच इनपुट के बिना छोटी स्क्रीन
  • बहुत सीमित मेमोरी

लेकिन वे एक मॉडर्न ब्राउज़र चलाते हैं और बहुत किफ़ायती हैं. इस वजह से, फ़ीचर फ़ोन तेज़ी से उभर रहे हैं. उनका प्राइस पॉइंट, ऐसे नए ऑडियंस को ऑनलाइन आने और मॉडर्न वेब का इस्तेमाल करने का मौका देता है जो पहले इसे वहन नहीं कर सकते थे. साल 2019 में यह अनुमान लगाया गया है कि सिर्फ़ भारत में ही करीब 40 करोड़ फ़ीचर फ़ोन बेचे जाएंगे. इसलिए, फ़ीचर फ़ोन इस्तेमाल करने वाले उपयोगकर्ता आपके दर्शकों का एक अहम हिस्सा बन सकते हैं. इसके अलावा, 2G जैसे कनेक्शन की स्पीड, उभरते हुए मार्केट में आम बात है. फ़ीचर फ़ोन पर बेहतर अनुभव दिलाने के लिए, हमने PROXX को कैसे मैनेज किया?

PROXX गेमप्ले.

परफ़ॉर्मेंस अहम है. साथ ही, इसमें लोडिंग परफ़ॉर्मेंस और रनटाइम की परफ़ॉर्मेंस, दोनों शामिल होती हैं. यह देखा गया है कि अच्छी परफ़ॉर्मेंस, उपयोगकर्ता को अपने साथ जोड़े रखने की दर, कन्वर्ज़न बढ़ाने, और सबसे ज़्यादा इनक्लूसिविटी में बढ़ोतरी से जुड़ी है. जेरेमी वैगनर के पास इस बारे में काफ़ी ज़्यादा डेटा और अहम जानकारी है कि परफ़ॉर्मेंस ज़रूरी क्यों है.

यह दो हिस्सों वाली सीरीज़ का पहला हिस्सा है. पहला चरण, लोड होने की परफ़ॉर्मेंस पर फ़ोकस करता है और दूसरा हिस्सा रनटाइम की परफ़ॉर्मेंस पर फ़ोकस करता है.

हालातों को समझाना

असल डिवाइस पर, लोड होने की परफ़ॉर्मेंस की जांच करना बहुत ज़रूरी है. अगर आपके पास असली डिवाइस नहीं है, तो हमारा सुझाव है कि आप WebPageTest का इस्तेमाल करें, खास तौर पर इसे "आसान" सेटअप के तौर पर इस्तेमाल करें. WPT, एम्युलेट किए गए 3G कनेक्शन वाले असली डिवाइस पर, बैटरी के लोड होने की जांच करता है.

3G नेटवर्क को मापने की स्पीड अच्छी होती है. भले ही आपका फ़ोन 4G, LTE या 5G का हो सकता है, लेकिन मोबाइल इंटरनेट की हकीकत इससे बिलकुल अलग है. हो सकता है कि आप ट्रेन में, कॉन्फ़्रेंस में, किसी कॉन्सर्ट में या फ़्लाइट में हों. आपको वहां जो अनुभव होगा वह 3G के करीब है, और कभी-कभी उससे भी खराब.

हालांकि, हम इस लेख में 2G नेटवर्क पर फ़ोकस करने जा रहे हैं, क्योंकि PROXX अपनी टारगेट ऑडियंस में फ़ीचर फ़ोन और उभरते बाज़ारों को साफ़ तौर पर टारगेट कर रहा है. WebPageTest की जांच हो जाने के बाद, आपको एक वॉटरफ़ॉल (जैसा कि आपने DevTools में दिखाया है) और सबसे ऊपर फ़िल्मस्ट्रिप दिखाई देती है. फ़िल्म स्ट्रिप से पता चलता है कि ऐप्लिकेशन के लोड होने के दौरान, उपयोगकर्ता को क्या दिखता है. 2G पर, PROXX के ऑप्टिमाइज़ न किए गए वर्शन का लोड करने का अनुभव बहुत खराब है:

फ़िल्मस्ट्रिप के वीडियो से पता चलता है कि जब PROXX किसी एम्युलेट किए गए 2G कनेक्शन पर, किसी असल और लो-एंड डिवाइस पर लोड होता है, तो उपयोगकर्ता को क्या दिखता है.

3G पर लोड होने पर, उपयोगकर्ता को 4 सेकंड तक बिना किसी सफ़ेद रंग वाली स्क्रीन दिखती है. 2G से ज़्यादा होने पर, उपयोगकर्ता को आठ सेकंड से ज़्यादा समय तक कुछ नहीं दिखता. अगर आपने परफ़ॉर्मेंस ज़रूरी क्यों है लेख पढ़ा है, तो आपको पता होगा कि अधीरता की वजह से अब हम अपने संभावित उपयोगकर्ताओं का एक अच्छा हिस्सा खो चुके हैं. स्क्रीन पर कोई भी चीज़ दिखाने के लिए, उपयोगकर्ता को 62 केबी का पूरा JavaScript डाउनलोड करना होगा. इस स्थिति में सबसे बड़ी बात यह है कि स्क्रीन पर जो भी दूसरी चीज़ दिखती है वह इंटरैक्टिव होती है. या फिर ऐसा होना चाहिए?

PROXX के ऑप्टिमाइज़ नहीं किए गए वर्शन में [फ़र्स्ट मीनिंगफ़ुल पेंट][FMP] _तकनीकी रूप से_ [इंटरैक्टिव][TTI] है, लेकिन यह लोगों के लिए किसी काम का नहीं है.

करीब 62 केबी gzip'd JS को डाउनलोड करने और DOM जनरेट करने के बाद, उपयोगकर्ता को हमारा ऐप्लिकेशन दिखाई देता है. यह ऐप्लिकेशन तकनीकी रूप से इंटरैक्टिव है. हालांकि, विज़ुअल देखने पर इसकी सच्चाई थोड़ी अलग हो जाती है. वेब फ़ॉन्ट अब भी बैकग्राउंड में लोड होते रहते हैं और जब तक वे तैयार नहीं हो जाते, तब तक उपयोगकर्ता को कोई टेक्स्ट नहीं दिखता. इस स्थिति को फ़र्स्ट मीनिंगफ़ुल पेंट (एफ़एमपी) कहा जाता है, लेकिन यह बिलकुल इंटरैक्टिव के तौर पर सही नहीं है. इसकी वजह यह है कि उपयोगकर्ता यह नहीं बता सकता कि कोई भी इनपुट किस बारे में है. 3G पर यह दूसरा सेकंड और 2G पर 3 सेकंड लगता है. इसके बाद, ऐप्लिकेशन इस्तेमाल के लिए तैयार हो जाता है. कुल मिलाकर, ऐप्लिकेशन को 3G पर 6 सेकंड और 2G पर 11 सेकंड लगते हैं.

वॉटरफ़ॉल विश्लेषण

अब जब हमें पता है कि उपयोगकर्ता को क्या दिखता है, तो हमें इसकी वजह पता करनी होगी. इसके लिए, हम वॉटरफ़ॉल को देखकर यह विश्लेषण कर सकते हैं कि संसाधन ज़्यादा देरी से क्यों लोड हो रहे हैं. PROXX के लिए हमारे 2G ट्रेस में हम दो मुख्य लाल फ़्लैग देख सकते हैं:

  1. इनमें कई रंगों की पतली लाइनें होती हैं.
  2. JavaScript फ़ाइलें एक चेन बनाती हैं. उदाहरण के लिए, दूसरा संसाधन पहला संसाधन खत्म होने के बाद ही लोड होना शुरू होता है और तीसरा संसाधन दूसरा संसाधन के खत्म होने के बाद ही शुरू होता है.
वॉटरफ़ॉल से इस बात की जानकारी मिलती है कि कौनसे संसाधन कब लोड हो रहे हैं और उनमें कितना समय लग रहा है.

कनेक्शन की संख्या कम की जा रही है

हर थिन लाइन (dns, connect, ssl) का मतलब नया एचटीटीपी कनेक्शन बनाना है. नया कनेक्शन सेट अप करना महंगा होता है, क्योंकि इसमें 3G पर 1 सेकंड और 2G पर करीब 2.5 सेकंड लगते हैं. हमारे वॉटरफ़ॉल में, हमें इनके लिए एक नया कनेक्शन दिखता है:

  • अनुरोध #1: हमारा index.html
  • अनुरोध #5: fonts.googleapis.com की फ़ॉन्ट स्टाइल
  • अनुरोध #8: Google Analytics
  • अनुरोध #9: fonts.gstatic.com की एक फ़ॉन्ट फ़ाइल
  • अनुरोध #14: वेब ऐप्लिकेशन मेनिफ़ेस्ट

index.html के लिए नए कनेक्शन से बचा नहीं जा सकता. ब्राउज़र को कॉन्टेंट पाने के लिए, हमारे सर्वर से कनेक्शन बनाना ज़रूरी है. कम से कम Analytics जैसी किसी चीज़ को इनलाइन करके, Google Analytics के नए कनेक्शन से बचा जा सकता है, लेकिन Google Analytics हमारे ऐप्लिकेशन को रेंडरिंग या इंटरैक्टिव बनने से नहीं रोक रहा है, इसलिए हम इस बात पर ध्यान नहीं देते कि यह कितनी तेज़ी से लोड हो रहा है. आम तौर पर, Google Analytics को उस समय लोड किया जाना चाहिए, जब बाकी सब कुछ पहले ही लोड हो चुका हो. इस तरह, यह शुरुआती लोड के दौरान बैंडविथ या प्रोसेसिंग पावर पर काम नहीं करेगा. वेब ऐप्लिकेशन मेनिफ़ेस्ट के लिए नया कनेक्शन, फ़ेच करने की खास जानकारी में तय किया गया है, क्योंकि मेनिफ़ेस्ट को बिना क्रेडेंशियल वाले कनेक्शन पर लोड करना पड़ता है. दोबारा, वेब ऐप्लिकेशन मेनिफ़ेस्ट हमारे ऐप्लिकेशन को रेंडरिंग या इंटरैक्टिव होने से नहीं रोकता है, इसलिए हमें इस पर ज़्यादा ध्यान देने की ज़रूरत नहीं है.

हालांकि, दो फ़ॉन्ट और उनकी स्टाइल एक समस्या हैं, क्योंकि वे रेंडरिंग और इंटरैक्टिविटी को भी ब्लॉक कर देते हैं. अगर हम उस सीएसएस को देखें जिसे fonts.googleapis.com से डिलीवर किया गया है, तो आपको बस दो @font-face नियम मिलेंगे. हर फ़ॉन्ट के लिए एक नियम होना चाहिए. फ़ॉन्ट स्टाइल असल में इतनी छोटी हैं कि हमने एक गै़र-ज़रूरी कनेक्शन को हटाकर, इसे अपने एचटीएमएल में इनलाइन करने का फ़ैसला किया है. फ़ॉन्ट फ़ाइलों के लिए, कनेक्शन सेटअप करने में आने वाली लागत से बचने के लिए, हम उन्हें अपने सर्वर पर कॉपी कर सकते हैं.

साथ-साथ लोड होने की प्रोसेस

अगर वॉटरफ़ॉल को देखकर हमें पता चलता है कि JavaScript फ़ाइल के लोड होने के बाद, नई फ़ाइलें तुरंत लोड होने लगती हैं. ऐसा आम तौर पर मॉड्यूल डिपेंडेंसी के लिए होता है. शायद हमारे मुख्य मॉड्यूल में स्टैटिक इंपोर्ट शामिल हैं, इसलिए JavaScript को तब तक नहीं चलाया जा सकता, जब तक कि उन इंपोर्ट के लोड नहीं हो जाते. यहां इस बात पर ध्यान देना ज़रूरी है कि इस तरह की डिपेंडेंसी, बिल्ड के समय पता चलती हैं. हम <link rel="preload"> टैग का इस्तेमाल करके यह पक्का कर सकते हैं कि एचटीएमएल मिलने के तुरंत बाद, सभी डिपेंडेंसी लोड होना शुरू हो जाएं.

नतीजे

आइए, देखते हैं कि हमने जो बदलाव किए हैं उनसे क्या हासिल हुआ. हमारे जांच सेटअप में ऐसे किसी भी वैरिएबल को बदलना ज़रूरी नहीं है जिससे नतीजों पर असर पड़ सकता है. इसलिए, हम इस लेख के बाकी हिस्से में WebPageTest के आसान सेटअप का इस्तेमाल करेंगे और फ़िल्मस्ट्रिप देखेंगे:

हम WebPageTest की फ़िल्मस्ट्रिप का इस्तेमाल करके यह देखते हैं कि हमारे बदलावों से क्या हासिल हुआ है.

इन बदलावों से हमारी टीटीआई की संख्या घटकर 11 हो गई है और यह 8.5 हो गई है. यह कनेक्शन सेटअप करने में लगने वाले हमारे टारगेट का करीब 2.5 सेकंड है. बहुत बढ़िया.

प्रीरेंडरिंग

हालांकि, हमने सिर्फ़ टीटीआई को कम किया है, लेकिन असल में उपयोगकर्ता की 8.5 सेकंड तक लंबी व्हाइट स्क्रीन पर कोई असर नहीं पड़ा है. यह मान लिया जाए कि एफ़एमपी को बेहतर बनाने में सबसे बड़ा सुधार, index.html में स्टाइल वाले मार्कअप भेजकर किया जा सकता है. इसे हासिल करने की आम तकनीकों के तौर पर, प्रीरेंडरिंग और सर्वर-साइड रेंडरिंग शामिल हैं. ये दोनों एक-दूसरे से काफ़ी हद तक जुड़े हैं. इनके बारे में वेब पर रेंडरिंग में बताया गया है. दोनों तकनीकें, नोड में वेब ऐप्लिकेशन को चलाती हैं और नतीजे देने वाले DOM को एचटीएमएल में सीरियलाइज़ करती हैं. सर्वर-साइड रेंडरिंग ऐसा अपने अनुरोध के हिसाब से, सर्वर साइड पर करती है. वहीं, प्रीरेंडरिंग की सुविधा, बिल्ड के समय ऐसा करती है और आउटपुट को आपके नए index.html के तौर पर सेव करती है. PROXX एक JAMStack ऐप्लिकेशन है और इसका कोई सर्वर साइड नहीं है. इसलिए, हमने प्रीरेंडरिंग की सुविधा लागू करने का फ़ैसला किया है.

प्रीरेंडरिंग करने वाली सुविधा को लागू करने के कई तरीके हैं. PROXX में हमने Puppeteer को इस्तेमाल करने का विकल्प चुना है, जो बिना किसी यूज़र इंटरफ़ेस के Chrome को शुरू करता है और आपको Node API की मदद से उस इंस्टेंस को रिमोट तरीके से कंट्रोल करने की सुविधा देता है. इसका इस्तेमाल हम अपने मार्कअप और JavaScript को इंजेक्ट करने के लिए करते हैं. इसके बाद, डीओएम को एचटीएमएल की स्ट्रिंग के तौर पर पढ़ते हैं. हम सीएसएस मॉड्यूल का इस्तेमाल कर रहे हैं. इसलिए, हमें ज़रूरी स्टाइल के लिए सीएसएस इनलाइनिंग मुफ़्त में मिलती है.

  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.setContent(rawIndexHTML);
  await page.evaluate(codeToRun);
  const renderedHTML = await page.content();
  browser.close();
  await writeFile("index.html", renderedHTML);

ऐसा होने से, हमें अपने एफ़एमपी में सुधार की उम्मीद हो सकती है. हमें अब भी JavaScript को पहले की तरह ही लोड और एक्ज़ीक्यूट करना है. इसलिए, हमें TTI में बहुत ज़्यादा बदलाव की उम्मीद नहीं करनी चाहिए. अगर कुछ भी हो, तो हमारा index.html बढ़ गया है और यह हमारे टीटीआई को थोड़ा पीछे ले जा सकता है. इसका पता लगाने का सिर्फ़ एक तरीका है: WebPageTest चलाना.

फ़िल्मस्ट्रिप में, हमारी एफ़एमपी मेट्रिक में साफ़ तौर पर सुधार दिखाया गया है. टीटीआई पर ज़्यादातर असर नहीं पड़ता.

हमारा पहला सार्थक पेंट 8.5 सेकंड से बदलकर 4.9 सेकंड हो गया है, जो कि एक बहुत बड़ा सुधार है. हमारा TTI अब भी लगभग 8.5 सेकंड पर होता है, इसलिए इस परिवर्तन से काफ़ी हद तक उस पर कोई असर नहीं पड़ा है. हमने यहां जो किया, वह सिद्धांत बदलाव है. कुछ लोग इसे हाथ की ढीली भी कह सकते हैं. गेम के इंटरमीडिएट विज़ुअल को रेंडर करके, हम ऐप्लिकेशन लोड होने की अनुमानित परफ़ॉर्मेंस को बेहतर बनाने के लिए इसमें बदलाव कर रहे हैं.

इनलाइन करना

DevTools और WebPageTest दोनों से मिलने वाली एक और मेट्रिक, हमें टाइम टू फ़र्स्ट बाइट (TTFB) है. यह अनुरोध के पहले बाइट से लेकर मिलने वाले जवाब के पहले बाइट तक लगने वाला समय होता है. आम तौर पर, इस समय को दोतरफ़ा यात्रा का समय (आरटीटी) भी कहा जाता है. हालांकि, तकनीकी रूप से इन दोनों संख्याओं में अंतर होता है. आरटीटी में सर्वर साइड पर अनुरोध की प्रोसेस होने में लगने वाला समय शामिल नहीं होता. DevTools और WebPageTest, अनुरोध/प्रतिक्रिया ब्लॉक के अंदर हल्के रंग से TTFB को विज़ुअलाइज़ करते हैं.

अनुरोध का लाइट सेक्शन बताता है कि अनुरोध को, जवाब का पहला बाइट मिलने का इंतज़ार है.

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

यह समस्या मूल रूप से HTTP/2 Push के लिए ही बनाई गई थी. ऐप्लिकेशन डेवलपर यह जानता है कि कुछ संसाधनों की ज़रूरत है और वे उन्हें नीचे धकेल सकते हैं. जब तक क्लाइंट को यह महसूस होता है कि उसे ज़्यादा रिसॉर्स फ़ेच करने की ज़रूरत है, तब तक वे ब्राउज़र की कैश मेमोरी में पहले से मौजूद रहते हैं. एचटीटीपी/2 पुश की सुविधा का इस्तेमाल करना मुश्किल होता है. इसलिए, इसे सही नहीं माना जाता. एचटीटीपी/3 के मानक तय करने के दौरान इस सवाल वाले स्पेस में फिर से विज़िट किया जाएगा. फ़िलहाल, सभी ज़रूरी संसाधनों को इनलाइन करना सबसे आसान है. ऐसा करने पर, आपको कैश मेमोरी में डेटा को कैश मेमोरी में सेव करने में आसानी होगी.

सीएसएस मॉड्यूल और Puppeteer पर आधारित हमारे प्रीरेंडरिंग की वजह से, हमारी ज़रूरी सीएसएस को पहले से ही इनलाइन कर दिया गया है. JavaScript के लिए हमें अपने ज़रूरी मॉड्यूल और उनकी डिपेंडेंसी को इनलाइन करना होगा. आप जिस बंडलर का इस्तेमाल कर रहे हैं उसके हिसाब से, इस टास्क में मुश्किलें अलग-अलग हैं.

JavaScript इनलाइन होने की वजह से, हमने टीटीआई को 8.5 से घटाकर 7.2 कर दिया है.

यह वीडियो हमारे टीटीआई से एक सेकंड दूर हो गया. अब हम उस पॉइंट पर पहुंच गए हैं जहां हमारे index.html में वह सारी चीज़ें मौजूद हैं जो शुरुआती रेंडर और इंटरैक्टिव बनाने के लिए ज़रूरी है. एचटीएमएल डाउनलोड होने के दौरान भी रेंडर हो सकता है. इससे हमारा एफ़एमपी बनता है. एचटीएमएल को पार्स और एक्ज़ीक्यूट करते ही, ऐप्लिकेशन इंटरैक्टिव हो जाता है.

कोड को ज़्यादा सख्ती से बांटना

हां, हमारे index.html में वह सब कुछ है जो इंटरैक्टिव बनने के लिए ज़रूरी है. लेकिन बारीकी से जांच करने पर, हमें पता चलता है कि इसमें बाकी सारी चीज़ें भी मौजूद हैं. हमारे index.html का साइज़ 43 केबी के आस-पास है. शुरुआत में उपयोगकर्ता जिस चीज़ के साथ इंटरैक्ट कर सकता है उसके बारे में जानने के लिए, हमारे पास गेम को कॉन्फ़िगर करने का एक फ़ॉर्म है. इसमें कुछ कॉम्पोनेंट, स्टार्ट बटन, और उपयोगकर्ता सेटिंग को बनाए रखने और लोड करने के लिए कुछ कोड शामिल होते हैं. यह तो हो गया. 43 केबी का साइज़ बहुत बड़ा होता है.

PROXX का लैंडिंग पेज. यहां सिर्फ़ ज़रूरी कॉम्पोनेंट का इस्तेमाल किया जाता है.

बंडल का साइज़ कहां से आ रहा है, यह जानने के लिए हम सोर्स मैप एक्सप्लोरर या इससे मिलते-जुलते टूल का इस्तेमाल कर सकते हैं. इससे, यह जानकारी मिलती है कि बंडल में क्या-क्या शामिल है. अनुमान के मुताबिक, हमारे बंडल में गेम लॉजिक, रेंडरिंग इंजन, जीतने वाली स्क्रीन, हारने वाली स्क्रीन, और कई सुविधाएं शामिल हैं. लैंडिंग पेज के लिए इन मॉड्यूल के सिर्फ़ एक छोटे सबसेट की ज़रूरत है. इंटरैक्टिविटी के लिए ज़रूरी नहीं होने वाली सभी चीज़ों को लेज़ी-लोड होने वाले मॉड्यूल में ले जाने से टीटीआई काफ़ी हद तक कम हो जाएगा.

PROXX के `index.html` कॉन्टेंट का विश्लेषण करने पर, कई ग़ैर-ज़रूरी रिसॉर्स का पता चलता है. अहम संसाधन हाइलाइट किए गए हैं.

इसके लिए हमें कोड स्प्लिट करना होगा. कोड को अलग-अलग करने से, आपके मोनोलिथिक बंडल को ऐसे छोटे-छोटे हिस्सों में बांट दिया जाता है जिन्हें मांग पर लेज़ी लोड किया जा सकता है. Webpack, Rollup, और Parcel जैसे लोकप्रिय बंडलर, डाइनैमिक import() का इस्तेमाल करके कोड को बांटने की सुविधा देते हैं. बंडलर आपके कोड का विश्लेषण करेगा और स्टैटिक रूप से इंपोर्ट किए गए सभी मॉड्यूल को इनलाइन करेगा. डाइनैमिक तौर पर इंपोर्ट की गई हर फ़ाइल को उसकी फ़ाइल में रखा जाएगा. साथ ही, उसे नेटवर्क से सिर्फ़ तब फ़ेच किया जाएगा, जब import() कॉल शुरू हो जाएगा. नेटवर्क पर जाने की अपनी लागत होती है और इसे सिर्फ़ तब करना चाहिए, जब आपके पास समय हो. यहां मंत्र है कि उन मॉड्यूल को स्टैटिक रूप से इंपोर्ट करें जो लोड होने के दौरान गंभीर ज़रूरी हों और बाकी सब चीज़ों को डाइनैमिक तौर पर लोड करें. हालांकि, लेज़ी-लोड मॉड्यूल के आखिरी मौके का इंतज़ार न करें, क्योंकि शायद कुछ ही समय में इस्तेमाल किए जाने वाले हैं. Phil Walton की Idle Only Ungent किताब, जिस पर लेज़ी लोडिंग और ईगर लोडिंग के बीच का समय है, उसे अच्छे तरीके से दिखाने का एक बेहतरीन तरीका है.

PROXX में हमने एक lazy.js फ़ाइल बनाई है, जो स्टैटिक रूप से हर वह चीज़ इंपोर्ट करती है जिसकी हमें ज़रूरत नहीं है. इसके बाद, हम अपनी मुख्य फ़ाइल में lazy.js को डाइनैमिक तौर पर इंपोर्ट कर सकते हैं. हालांकि, हमारे कुछ Preact कॉम्पोनेंट lazy.js में आ गए हैं, जो थोड़ी मुश्किल हो सकते हैं. ऐसा इसलिए है, क्योंकि Preact, धीमे से लोड होने वाले कॉम्पोनेंट को पूरा नहीं कर पाता. इस वजह से हमने deferred कॉम्पोनेंट का एक छोटा रैपर लिखा. इसकी मदद से, हम प्लेसहोल्डर को तब तक रेंडर कर पाते हैं, जब तक कि असल कॉम्पोनेंट लोड नहीं हो जाता.

export default function deferred(componentPromise) {
  return class Deferred extends Component {
    constructor(props) {
      super(props);
      this.state = {
        LoadedComponent: undefined
      };
      componentPromise.then(component => {
        this.setState({ LoadedComponent: component });
      });
    }

    render({ loaded, loading }, { LoadedComponent }) {
      if (LoadedComponent) {
        return loaded(LoadedComponent);
      }
      return loading();
    }
  };
}

इसकी जगह, हम अपने render() फ़ंक्शन में कॉम्पोनेंट के प्रॉमिस का इस्तेमाल कर सकते हैं. उदाहरण के लिए, कॉम्पोनेंट के लोड होने के दौरान, <Nebula> कॉम्पोनेंट को खाली <div> से बदल दिया जाएगा, जो ऐनिमेशन वाली बैकग्राउंड इमेज को रेंडर करता है. कॉम्पोनेंट के लोड हो जाने और इस्तेमाल के लिए तैयार हो जाने पर, <div> को असल कॉम्पोनेंट से बदल दिया जाएगा.

const NebulaDeferred = deferred(
  import("/components/nebula").then(m => m.default)
);

return (
  // ...
  <NebulaDeferred
    loading={() => <div />}
    loaded={Nebula => <Nebula />}
  />
);

इन सभी सुविधाओं को ध्यान में रखते हुए, हमने अपने index.html को घटाकर 20 केबी कर दिया, जो कि मूल साइज़ के आधे से भी कम है. एफ़एमपी और टीटीआई पर इसका क्या असर होता है? WebPageTest से हमें पता चलेगा!

फ़िल्मस्ट्रिप से पता चलता है कि हमारा टीटीआई अब 5.4 सेकंड पर है. हमारे शुरुआती 11 सेकंड से बहुत ही बेहतर सुधार.

हमारे एफ़एमपी और टीटीआई के बीच सिर्फ़ 100 मि॰से॰ का अंतर है, क्योंकि इसमें सिर्फ़ इनलाइन JavaScript को पार्स और एक्ज़ीक्यूट करना है. 2G पर सिर्फ़ 5.4 सेकंड के बाद, यह ऐप पूरी तरह से इंटरैक्टिव हो जाता है. बाकी सभी, कम ज़रूरी मॉड्यूल, बैकग्राउंड में लोड होते हैं.

हाथ की ज़्यादा सफ़ाई

अगर आप ऊपर दी गई ज़रूरी मॉड्यूल की हमारी सूची देखें, तो आप पाएंगे कि रेंडरिंग इंजन ज़रूरी मॉड्यूल का हिस्सा नहीं है. बेशक, गेम तब तक शुरू नहीं हो सकता, जब तक हमारे पास गेम को रेंडर करने के लिए हमारा रेंडरिंग इंजन न हो. हम "शुरू करें" बटन को तब तक बंद कर सकते हैं, जब तक कि हमारा रेंडरिंग इंजन गेम शुरू करने के लिए तैयार नहीं हो जाता. हालांकि, हमारे अनुभव में, उपयोगकर्ता आम तौर पर अपनी गेम सेटिंग कॉन्फ़िगर करने में काफ़ी समय लेता है. हालांकि, यह ज़रूरी नहीं है. ज़्यादातर समय, रेंडरिंग इंजन और बाकी बचे मॉड्यूल तब तक लोड हो जाते हैं, जब उपयोगकर्ता "शुरू करें" बटन को दबाता है. बहुत कम मामलों में, जब उपयोगकर्ता अपने इंटरनेट कनेक्शन की स्पीड से तेज़ होता है, तब हम लोड होने वाली एक सामान्य स्क्रीन दिखाते हैं, जो बाकी मॉड्यूल के खत्म होने का इंतज़ार करती है.

नतीजा

मेज़र करना ज़रूरी है. जो समस्याएं असल नहीं होती उनमें समय बिताने से बचने के लिए, हमारा सुझाव है कि ऑप्टिमाइज़ेशन लागू करने से पहले, हमेशा पहले आकलन करें. इसके अलावा, अगर आपके पास कोई डिवाइस न हो, तो 3G कनेक्शन वाले असली डिवाइसों पर या WebPageTest पर ये मेज़रमेंट किए जाने चाहिए.

फ़िल्मस्ट्रिप से आपको इस बारे में अहम जानकारी मिल सकती है कि उपयोगकर्ता के लिए आपका ऐप्लिकेशन लोड होने का महसूस कैसा होता है. वॉटरफ़ॉल से आपको यह पता चल सकता है कि किन संसाधनों की वजह से, लोड होने में ज़्यादा समय लग सकता है. यहां कुछ चीज़ों की चेकलिस्ट दी गई है, जिन्हें अपनाकर परफ़ॉर्मेंस को बेहतर बनाया जा सकता है:

  • एक कनेक्शन पर ज़्यादा से ज़्यादा एसेट डिलीवर करें.
  • पहले से लोड करना या इनलाइन रिसॉर्स भी, जो पहली इमेज को रेंडर करने और इंटरैक्टिविटी के लिए ज़रूरी हैं.
  • लोड होने की अनुमानित परफ़ॉर्मेंस को बेहतर बनाने के लिए, अपने ऐप्लिकेशन को पहले से रेंडर करें.
  • इंटरैक्टिविटी के लिए ज़रूरी कोड की मात्रा को कम करने के लिए, ज़्यादा बारीकी से कोड को अलग-अलग हिस्सों में बांटने की सुविधा का इस्तेमाल करें.

दूसरे चरण को जानने के लिए हमारे साथ बने रहें. इसमें हमने उन डिवाइसों पर रनटाइम की परफ़ॉर्मेंस को ऑप्टिमाइज़ करने के तरीके बताए हैं जिन पर सीमित सुविधाओं का इस्तेमाल किया जा सकता है.