बड़े JavaScript संसाधनों को लोड करने से, पेज की स्पीड पर काफ़ी असर पड़ता है. अपने JavaScript को छोटे-छोटे हिस्सों में बांटकर, स्टार्टअप के दौरान सिर्फ़ ज़रूरी कॉन्टेंट को डाउनलोड करने से, आपके पेज के लोड होने में लगने वाले समय को काफ़ी हद तक कम किया जा सकता है. इससे आपके पेज के इंटरैक्शन टू नेक्स्ट पेंट (आईएनपी) को भी बेहतर बनाया जा सकता है.
जब कोई पेज बड़ी JavaScript फ़ाइलों को डाउनलोड, पार्स, और कंपाइल करता है, तो कुछ समय के लिए वह पेज काम नहीं कर सकता. पेज के एलिमेंट दिखते हैं, क्योंकि वे पेज के शुरुआती एचटीएमएल का हिस्सा होते हैं और सीएसएस से स्टाइल किए जाते हैं. हालांकि, इन इंटरैक्टिव एलिमेंट के साथ-साथ पेज पर लोड की गई अन्य स्क्रिप्ट को काम करने के लिए, JavaScript की ज़रूरत होती है. इसलिए, हो सकता है कि इन एलिमेंट के काम करने के लिए, JavaScript को पार्स और लागू किया जा रहा हो. इस वजह से, उपयोगकर्ता को ऐसा लग सकता है कि इंटरैक्शन में काफ़ी देरी हुई है या वह पूरी तरह से बंद हो गया है.
आम तौर पर, ऐसा इसलिए होता है, क्योंकि मुख्य थ्रेड ब्लॉक हो जाती है. ऐसा इसलिए होता है, क्योंकि JavaScript को मुख्य थ्रेड पर पार्स और कंपाइल किया जाता है. अगर इस प्रोसेस में ज़्यादा समय लगता है, तो हो सकता है कि इंटरैक्टिव पेज एलिमेंट, उपयोगकर्ता के इनपुट का तुरंत जवाब न दे पाएं. इस समस्या को ठीक करने के लिए, सिर्फ़ वही JavaScript लोड करें जो पेज के काम करने के लिए ज़रूरी है. साथ ही, कोड को अलग-अलग हिस्सों में बांटने की तकनीक का इस्तेमाल करके, बाकी JavaScript को बाद में लोड करें. इस मॉड्यूल में, इन दोनों में से दूसरी तकनीक पर फ़ोकस किया गया है.
कोड को अलग-अलग करने की सुविधा का इस्तेमाल करके, स्टार्टअप के दौरान JavaScript को पार्स करने और उसे लागू करने में लगने वाले समय को कम करना
Lighthouse तब चेतावनी दिखाता है, जब JavaScript को लागू करने में दो सेकंड से ज़्यादा समय लगता है.साथ ही, तीन सेकंड से ज़्यादा समय लगने पर, यह काम पूरा नहीं होता. पेज के लाइफ़साइकल के किसी भी समय पर, ज़्यादा JavaScript पार्स और एक्सीक्यूशन एक संभावित समस्या है. ऐसा इसलिए, क्योंकि अगर उपयोगकर्ता उस समय पेज से इंटरैक्ट करता है, जब JavaScript को प्रोसेस और एक्सीक्यूट करने वाले मुख्य थ्रेड के टास्क चल रहे हों, तो इंटरैक्शन के इनपुट डिले में बढ़ोतरी हो सकती है.
इसके अलावा, शुरुआती पेज लोड के दौरान, ज़्यादा JavaScript को लागू करने और पार्स करने से खास तौर पर समस्या होती है. ऐसा इसलिए, क्योंकि पेज के लाइफ़साइकल में यह वह समय होता है जब उपयोगकर्ताओं के पेज के साथ इंटरैक्ट करने की संभावना ज़्यादा होती है. असल में, कुल ब्लॉकिंग समय (टीबीटी), लोड रिस्पॉन्सिविटी मेट्रिक है. यह INP से काफ़ी मेल खाती है. इससे पता चलता है कि उपयोगकर्ता, पेज के लोड होने के दौरान इंटरैक्शन करने की कोशिश करते हैं.
Lighthouse ऑडिट, आपके पेज के अनुरोध वाली हर JavaScript फ़ाइल को लागू करने में लगने वाले समय की जानकारी देता है. यह ऑडिट इस लिहाज़ से फ़ायदेमंद है कि इससे यह पता लगाया जा सकता है कि कौनसी स्क्रिप्ट कोड को अलग-अलग हिस्सों में बांटने के लिए सही हैं. इसके बाद, Chrome DevTools में कवरेज टूल का इस्तेमाल करके, यह पता लगाया जा सकता है कि पेज लोड होने के दौरान, पेज के JavaScript के किन हिस्सों का इस्तेमाल नहीं किया जाता.
कोड को अलग-अलग करने की सुविधा एक काम की तकनीक है. इससे किसी पेज के शुरुआती JavaScript पेलोड को कम किया जा सकता है. इसकी मदद से, किसी JavaScript बंडल को दो हिस्सों में बांटा जा सकता है:
- पेज लोड होने के दौरान ज़रूरी JavaScript, किसी और समय लोड नहीं किया जा सकता.
- बाकी JavaScript, बाद में लोड की जा सकती है. आम तौर पर, इसे तब लोड किया जाता है, जब उपयोगकर्ता पेज पर किसी इंटरैक्टिव एलिमेंट से इंटरैक्ट करता है.
डाइनैमिक import()
सिंटैक्स का इस्तेमाल करके, कोड को अलग-अलग हिस्सों में बांटा जा सकता है. यह सिंटैक्स, <script>
एलिमेंट से अलग है, जो स्टार्टअप के दौरान किसी दिए गए JavaScript रिसॉर्स का अनुरोध करता है. यह सिंटैक्स, पेज के लाइफ़साइकल के दौरान किसी JavaScript रिसॉर्स का अनुरोध करता है.
document.querySelectorAll('#myForm input').addEventListener('blur', async () => {
// Get the form validation named export from the module through destructuring:
const { validateForm } = await import('/validate-form.mjs');
// Validate the form:
validateForm();
}, { once: true });
ऊपर दिए गए JavaScript स्निपेट में, validate-form.mjs
मॉड्यूल को सिर्फ़ तब डाउनलोड, पार्स, और लागू किया जाता है, जब कोई उपयोगकर्ता फ़ॉर्म के किसी भी <input>
फ़ील्ड को धुंधला करता है. इस स्थिति में, फ़ॉर्म की पुष्टि करने के लॉजिक को चलाने के लिए ज़िम्मेदार JavaScript रिसॉर्स, पेज के साथ सिर्फ़ तब जुड़ा होता है, जब उसका इस्तेमाल होने की संभावना सबसे ज़्यादा होती है.
webpack, Parcel, Rollup, और esbuild जैसे JavaScript बंडलर को कॉन्फ़िगर किया जा सकता है, ताकि वे आपके सोर्स कोड में डाइनैमिक import()
कॉल मिलने पर, JavaScript बंडल को छोटे-छोटे हिस्सों में बांट सकें. इनमें से ज़्यादातर टूल, ऐसा अपने-आप करते हैं. हालांकि, esbuild के लिए आपको इस ऑप्टिमाइज़ेशन में ऑप्ट इन करना होगा.
कोड को अलग-अलग हिस्सों में बांटने के बारे में काम की जानकारी
कोड को अलग-अलग करने की सुविधा, शुरुआती पेज लोड के दौरान मुख्य थ्रेड के कॉन्टेंट को कम करने का एक असरदार तरीका है. हालांकि, अगर आपको कोड को अलग-अलग करने के अवसरों के लिए, अपने JavaScript सोर्स कोड का ऑडिट करना है, तो कुछ बातों का ध्यान रखें.
अगर हो सके, तो बंडलर का इस्तेमाल करें
डेवलपर, डेवलपमेंट प्रोसेस के दौरान JavaScript मॉड्यूल का इस्तेमाल आम तौर पर करते हैं. यह डेवलपर के अनुभव को बेहतर बनाने के लिए एक बेहतरीन सुविधा है. इससे कोड को पढ़ने और उसे मैनेज करने में आसानी होती है. हालांकि, JavaScript मॉड्यूल को प्रोडक्शन में शिप करने पर, परफ़ॉर्मेंस से जुड़ी कुछ समस्याएं आ सकती हैं.
सबसे ज़रूरी बात यह है कि आपको अपने सोर्स कोड को प्रोसेस और ऑप्टिमाइज़ करने के लिए, बंडलर का इस्तेमाल करना चाहिए. इसमें वे मॉड्यूल भी शामिल हैं जिनका कोड स्प्लिट करना है. बंडलर, JavaScript सोर्स कोड में ऑप्टिमाइज़ेशन लागू करने के साथ-साथ, परफ़ॉर्मेंस से जुड़ी बातों को भी संतुलित करने में काफ़ी असरदार होते हैं. जैसे, कंप्रेस करने के अनुपात के मुकाबले बंडल का साइज़. बंडल के साइज़ के साथ, कंप्रेस करने की क्षमता बढ़ती है. हालांकि, बंडलर यह भी पक्का करते हैं कि बंडल इतने बड़े न हों कि स्क्रिप्ट की जांच में ज़्यादा समय लगे.
बंडलर, नेटवर्क पर बड़ी संख्या में बिना बंडल किए गए मॉड्यूल शिप करने की समस्या से भी बचते हैं. JavaScript मॉड्यूल का इस्तेमाल करने वाले आर्किटेक्चर में, बड़े और जटिल मॉड्यूल ट्री होते हैं. मॉड्यूल ट्री को अनबंड करने पर, हर मॉड्यूल एक अलग एचटीटीपी अनुरोध दिखाता है. साथ ही, मॉड्यूल को बंडल न करने पर, आपके वेब ऐप्लिकेशन में इंटरैक्टिविटी में देरी हो सकती है. बड़े मॉड्यूल ट्री को जल्द से जल्द लोड करने के लिए, <link rel="modulepreload">
रिसॉर्स हिंट का इस्तेमाल किया जा सकता है. हालांकि, लोडिंग की परफ़ॉर्मेंस के लिहाज़ से, JavaScript बंडल का इस्तेमाल करना अब भी बेहतर है.
स्ट्रीमिंग कंपाइलेशन की सुविधा को गलती से बंद न करें
Chromium का V8 JavaScript इंजन, ऑप्टिमाइज़ेशन के कई तरीके पहले से ही उपलब्ध कराता है. इससे यह पक्का होता है कि आपका प्रोडक्शन JavaScript कोड, ज़्यादा से ज़्यादा बेहतर तरीके से लोड हो. इनमें से एक ऑप्टिमाइज़ेशन को स्ट्रीमिंग कंपाइलेशन कहा जाता है. यह ब्राउज़र पर स्ट्रीम किए गए एचटीएमएल की इंक्रीमेंटल पार्सिंग की तरह ही काम करता है. यह नेटवर्क से आने वाले JavaScript के स्ट्रीम किए गए हिस्सों को कंपाइल करता है.
यह पक्का करने के लिए कि Chromium में आपके वेब ऐप्लिकेशन के लिए स्ट्रीमिंग कंपाइलेशन की सुविधा काम करे, आपके पास ये तरीके हैं:
- JavaScript मॉड्यूल का इस्तेमाल न करने के लिए, अपने प्रोडक्शन कोड को बदलें. बंडलर, कंपाइलेशन टारगेट के आधार पर आपके JavaScript सोर्स कोड में बदलाव कर सकते हैं. आम तौर पर, टारगेट किसी खास एनवायरमेंट के लिए होता है. V8, स्ट्रीमिंग कंपाइलेशन की सुविधा को ऐसे किसी भी JavaScript कोड पर लागू करेगा जो मॉड्यूल का इस्तेमाल नहीं करता. साथ ही, अपने JavaScript मॉड्यूल कोड को ऐसे सिंटैक्स में बदलने के लिए, बंडलर को कॉन्फ़िगर किया जा सकता है जो JavaScript मॉड्यूल और उनकी सुविधाओं का इस्तेमाल नहीं करता.
- अगर आपको JavaScript मॉड्यूल को प्रोडक्शन में शिप करना है, तो
.mjs
एक्सटेंशन का इस्तेमाल करें. आपके प्रोडक्शन JavaScript में मॉड्यूल का इस्तेमाल किया जाता है या नहीं, इससे कोई फ़र्क़ नहीं पड़ता. मॉड्यूल का इस्तेमाल करने वाले JavaScript और न करने वाले JavaScript के लिए, कोई खास कॉन्टेंट टाइप नहीं होता. V8 के मामले में,.js
एक्सटेंशन का इस्तेमाल करके प्रोडक्शन में JavaScript मॉड्यूल शिप करने पर, स्ट्रीमिंग कंपाइलेशन से ऑप्ट आउट किया जाता है. अगर JavaScript मॉड्यूल के लिए.mjs
एक्सटेंशन का इस्तेमाल किया जाता है, तो V8 यह पक्का कर सकता है कि मॉड्यूल पर आधारित JavaScript कोड के लिए, स्ट्रीमिंग कंपाइलेशन काम कर रहा है या नहीं.
इन बातों को ध्यान में रखते हुए, कोड को अलग-अलग हिस्सों में बांटने की सुविधा का इस्तेमाल करना बंद न करें. कोड को अलग-अलग हिस्सों में बांटने से, उपयोगकर्ताओं को शुरुआती JavaScript पेलोड कम करने में मदद मिलती है. हालांकि, बंडलर का इस्तेमाल करके और V8 के स्ट्रीमिंग कंपाइलेशन व्यवहार को बनाए रखने का तरीका जानकर, यह पक्का किया जा सकता है कि आपका प्रोडक्शन JavaScript कोड, उपयोगकर्ताओं के लिए जितनी हो सके उतना तेज़ हो.
डाइनैमिक इंपोर्ट का डेमो
webpack
webpack, SplitChunksPlugin
नाम के प्लग इन के साथ शिप होता है. इसकी मदद से, यह कॉन्फ़िगर किया जा सकता है कि बंडलर, JavaScript फ़ाइलों को कैसे बांटता है. webpack, डाइनैमिक import()
और स्टैटिक import
स्टेटमेंट, दोनों को पहचानता है. SplitChunksPlugin
के कॉन्फ़िगरेशन में chunks
विकल्प तय करके, SplitChunksPlugin
के व्यवहार में बदलाव किया जा सकता है:
chunks: async
डिफ़ॉल्ट वैल्यू है और यह डाइनैमिकimport()
कॉल को रेफ़र करता है.chunks: initial
का मतलब स्टैटिकimport
कॉल से है.chunks: all
में डाइनैमिकimport()
और स्टैटिक इंपोर्ट, दोनों शामिल होते हैं. इससे आपकोasync
औरinitial
इंपोर्ट के बीच चंक शेयर करने की सुविधा मिलती है.
जब भी webpack को कोई डाइनैमिक import()
स्टेटमेंट मिलता है, तो वह डिफ़ॉल्ट रूप से उस मॉड्यूल के लिए एक अलग चंक बनाता है:
/* main.js */
// An application-specific chunk required during the initial page load:
import myFunction from './my-function.js';
myFunction('Hello world!');
// If a specific condition is met, a separate chunk is downloaded on demand,
// rather than being bundled with the initial chunk:
if (condition) {
// Assumes top-level await is available. More info:
// https://v8.dev/features/top-level-await
await import('/form-validation.js');
}
ऊपर दिए गए कोड स्निपेट के लिए, वेबपैक के डिफ़ॉल्ट कॉन्फ़िगरेशन से दो अलग-अलग चंक बनते हैं:
main.js
चंक, जिसे वेबपैकinitial
चंक के तौर पर बांटता है. इसमेंmain.js
और./my-function.js
मॉड्यूल शामिल होते हैं.async
चंक, जिसमें सिर्फ़form-validation.js
शामिल होता है. अगर कॉन्फ़िगर किया गया है, तो संसाधन के नाम में फ़ाइल हैश शामिल होता है. यह चंक सिर्फ़ तब डाउनलोड किया जाता है, जबcondition
सही हो.
इस कॉन्फ़िगरेशन की मदद से, form-validation.js
चंक को तब तक लोड करने से रोका जा सकता है, जब तक कि उसकी ज़रूरत न पड़े. इससे, पेज लोड होने के दौरान स्क्रिप्ट की जांच में लगने वाले समय को कम करके, पेज लोड होने में लगने वाले समय को कम किया जा सकता है. form-validation.js
चंक के लिए स्क्रिप्ट डाउनलोड और उसका आकलन तब होता है, जब कोई खास शर्त पूरी हो जाती है. इस मामले में, डाइनैमिक तौर पर इंपोर्ट किया गया मॉड्यूल डाउनलोड किया जाता है. उदाहरण के लिए, ऐसा हो सकता है कि कोई polyfill सिर्फ़ किसी खास ब्राउज़र के लिए डाउनलोड किया गया हो या—जैसा कि पिछले उदाहरण में बताया गया है—इंपोर्ट किया गया मॉड्यूल, उपयोगकर्ता इंटरैक्शन के लिए ज़रूरी हो.
दूसरी ओर, SplitChunksPlugin
कॉन्फ़िगरेशन को chunks: initial
के तौर पर तय करने से यह पक्का होता है कि कोड को सिर्फ़ शुरुआती चंक में बांटा जाए. ये ऐसे चंक होते हैं जिन्हें स्टैटिक तौर पर इंपोर्ट किया जाता है या वेबपैक की entry
प्रॉपर्टी में लिस्ट किए जाते हैं. पिछले उदाहरण को देखते हुए, नतीजा एक ही स्क्रिप्ट फ़ाइल में form-validation.js
और main.js
का कॉम्बिनेशन होगा. इससे, पेज लोड होने की शुरुआती परफ़ॉर्मेंस खराब हो सकती है.
SplitChunksPlugin
के विकल्पों को कॉन्फ़िगर करके, बड़ी स्क्रिप्ट को कई छोटी स्क्रिप्ट में भी बांटा जा सकता है. उदाहरण के लिए, maxSize
विकल्प का इस्तेमाल करके, webpack को निर्देश दें कि अगर चंक, maxSize
में बताई गई सीमा से ज़्यादा हो जाएं, तो उन्हें अलग-अलग फ़ाइलों में बांट दें. बड़ी स्क्रिप्ट फ़ाइलों को छोटी फ़ाइलों में बांटने से, लोड होने में लगने वाला समय कम हो सकता है. ऐसा इसलिए, क्योंकि कुछ मामलों में सीपीयू पर ज़्यादा लोड डालने वाली स्क्रिप्ट के आकलन के काम को छोटे टास्क में बांटा जाता है. इन टास्क की वजह से, मुख्य थ्रेड को लंबे समय तक ब्लॉक होने की संभावना कम होती है.
इसके अलावा, बड़ी JavaScript फ़ाइलें जनरेट करने का मतलब यह भी है कि स्क्रिप्ट के कैश मेमोरी में मौजूद कॉन्टेंट अमान्य होने की संभावना ज़्यादा होती है. उदाहरण के लिए, अगर फ़्रेमवर्क और पहले पक्ष के ऐप्लिकेशन कोड, दोनों के साथ बहुत बड़ी स्क्रिप्ट शिप की जाती है, तो सिर्फ़ फ़्रेमवर्क को अपडेट करने पर, बंडल किए गए रिसॉर्स में मौजूद बाकी चीज़ों के साथ-साथ पूरा बंडल अमान्य हो सकता है.
वहीं दूसरी ओर, छोटी स्क्रिप्ट फ़ाइलों से यह संभावना बढ़ जाती है कि वापस आने वाला व्यक्ति कैश मेमोरी से रिसॉर्स हासिल कर ले. इससे, बार-बार आने पर पेज तेज़ी से लोड होता है. हालांकि, बड़ी फ़ाइलों की तुलना में छोटी फ़ाइलों को कंप्रेस करने से कम फ़ायदा मिलता है. साथ ही, बिना प्राइम किए गए ब्राउज़र कैश मेमोरी की वजह से, पेज लोड होने पर नेटवर्क राउंड-ट्रिप टाइम बढ़ सकता है. कैश मेमोरी का इस्तेमाल करने की क्षमता, कॉम्प्रेस करने की क्षमता, और स्क्रिप्ट के आकलन में लगने वाले समय के बीच संतुलन बनाए रखना ज़रूरी है.
webpack डेमो
webpack SplitChunksPlugin
डेमो.
अपने ज्ञान को परखें
कोड को अलग-अलग हिस्सों में बांटते समय, किस तरह के import
स्टेटमेंट का इस्तेमाल किया जाता है?
import()
.import
.
JavaScript मॉड्यूल के सबसे ऊपर किस तरह का import
स्टेटमेंट होना चाहिए और किसी और जगह पर नहीं?
import()
.import
.
webpack में SplitChunksPlugin
का इस्तेमाल करते समय, async
और
initial
चंक के बीच क्या अंतर है?
async
चंक, डाइनैमिक import()
का इस्तेमाल करके लोड किए जाते हैं
और initial
चंक, स्टैटिक
import
का इस्तेमाल करके लोड किए जाते हैं.
async
चंक, स्टैटिक import
का इस्तेमाल करके लोड किए जाते हैं और initial
चंक, डाइनैमिक import()
का इस्तेमाल करके लोड किए जाते हैं.
अगला लेख: इमेज और <iframe>
एलिमेंट को लेज़ी लोड करना
हालांकि, यह एक ऐसा रिसॉर्स है जिसकी लागत काफ़ी ज़्यादा होती है, लेकिन JavaScript ही ऐसा रिसॉर्स नहीं है जिसे लोड होने में देरी की जा सकती है. इमेज और <iframe>
एलिमेंट, संभावित तौर पर महंगे संसाधन होते हैं. JavaScript की तरह ही, इमेज और <iframe>
एलिमेंट को धीरे-धीरे लोड करके, इन्हें लोड होने में लगने वाले समय को कम किया जा सकता है. इस बारे में इस कोर्स के अगले मॉड्यूल में बताया गया है.