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

हमने 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 के बिना ऑप्टिमाइज़ किए गए वर्शन को लोड करने में काफ़ी समय लगता है:

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

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

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

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

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

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

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

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

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

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

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

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

एक साथ कई यूआरएल लोड करना

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

नतीजे

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

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

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

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

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

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

  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 लोड और उसे लागू करना होगा. इसलिए, हमें उम्मीद नहीं है कि टीटीआई में काफ़ी बदलाव होगा. अगर कुछ भी हो, तो हमारा index.html बड़ा हो गया है और हो सकता है कि हमारा टीटीआई थोड़ा पीछे हो जाए. यह पता लगाने का सिर्फ़ एक तरीका है: WebPageTest चलाना.

फ़िल्मस्ट्रिप से पता चलता है कि हमारी FMP मेट्रिक में काफ़ी सुधार हुआ है. TTI पर इसका ज़्यादातर असर नहीं पड़ता.

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

इनलाइन करना

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

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

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

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

हमने 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() फ़ंक्शन में किसी कॉम्पोनेंट के Promise का इस्तेमाल कर सकते हैं. उदाहरण के लिए, ऐनिमेशन वाली बैकग्राउंड इमेज को रेंडर करने वाले <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 सेकंड है. यह हमारे ओरिजनल 11s की तुलना में काफ़ी बेहतर है.

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

ज़्यादा चालाकी

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

नतीजा

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

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

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

दूसरे हिस्से के लिए हमारे साथ बने रहें. इसमें हम कम रैम वाले डिवाइसों पर, रनटाइम की परफ़ॉर्मेंस को ऑप्टिमाइज़ करने के तरीके के बारे में बात करेंगे.