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

हमने 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 सेकंड का कनेक्शन सेटअप करने का समय है, जिसे हमने हटाने का लक्ष्य रखा था. बहुत बढ़िया.

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

हमने टीटीआई को कम कर दिया है. हालांकि, हमने उस सफ़ेद स्क्रीन पर कोई असर नहीं डाला है जिसे उपयोगकर्ता को 8.5 सेकंड तक देखना पड़ता है. आम तौर पर, एफ़एमपी में सबसे बड़े सुधार अपने index.html में अलग-अलग स्टाइल में मार्कअप करके किए जा सकते हैं. ऐसा करने की सामान्य तकनीकें, प्रीरेंडरिंग और सर्वर-साइड रेंडरिंग हैं. ये एक-दूसरे से काफ़ी हद तक संबंधित हैं. इनके बारे में वेब पर रेंडरिंग में बताया गया है. दोनों तकनीकें, वेब ऐप्लिकेशन को 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 में इनलाइनिंग की मदद से, हमने अपने TTI को 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 सेकंड है. 11वीं सदी के हमारे बेहतरीन वीडियो में किया गया एक शानदार सुधार.

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

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

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

नतीजा

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

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

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

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