स्क्रिप्ट लोड करते समय, ब्राउज़र को स्क्रिप्ट चलाने से पहले उनकी समीक्षा करने में समय लगता है. इस वजह से, लंबे टास्क बन सकते हैं. जानें कि स्क्रिप्ट इवैलुएशन कैसे काम करती है और पेज लोड के दौरान लंबे टास्क की वजह से इसे कैसे रोका जा सकता है.
इंटरैक्शन टू नेक्स्ट पेंट (आईएनपी) को ऑप्टिमाइज़ करने के लिए, आपको इंटरैक्शन को खुद ऑप्टिमाइज़ करने की सलाह मिलेगी. उदाहरण के लिए, लंबे टास्क को ऑप्टिमाइज़ करने की गाइड में, setTimeout
से मुनाफ़े को बढ़ाने जैसी तकनीकों के बारे में बताया गया है. साथ ही, अन्य तकनीकों के बारे में भी बताया गया है. ये तकनीक फ़ायदेमंद हैं, क्योंकि इससे मुख्य थ्रेड को लंबे टास्क करने से बचने की थोड़ी-बहुत जगह मिल जाती है. इससे बातचीत और दूसरी गतिविधि जल्दी करने के ज़्यादा मौके मिलते हैं, न कि सिर्फ़ एक लंबे टास्क के लिए इंतज़ार करना पड़ता.
हालांकि, स्क्रिप्ट लोड होने की वजह से जो लंबे टास्क होते हैं उनका क्या होता है? इन टास्क की वजह से, उपयोगकर्ता के इंटरैक्शन में रुकावट आ सकती है. साथ ही, लोड होने के दौरान किसी पेज की आईएनपी मेट्रिक पर भी असर पड़ सकता है. इस गाइड में बताया गया है कि ब्राउज़र, स्क्रिप्ट जांच के दौरान शुरू किए गए टास्क को कैसे मैनेज करते हैं. साथ ही, इस गाइड में यह भी बताया गया है कि स्क्रिप्ट के आकलन की प्रोसेस को अलग-अलग करने के लिए क्या किया जा सकता है. इससे पेज लोड होने के दौरान, उपयोगकर्ता के इनपुट के लिए आपका मुख्य थ्रेड बेहतर तरीके से काम कर पाएगा.
स्क्रिप्ट इवैलुएशन क्या है?
अगर आपने किसी ऐसे ऐप्लिकेशन की प्रोफ़ाइल बनाई है जो बहुत ज़्यादा JavaScript भेजता है, तो हो सकता है कि आपने लंबे टास्क देखे हों जिनमें अपराधी को स्क्रिप्ट का आकलन करें का लेबल दिया गया हो.
ब्राउज़र में JavaScript को चलाने के लिए, स्क्रिप्ट की जांच करना एक ज़रूरी हिस्सा है. इसकी वजह यह है कि लागू होने से ठीक पहले JavaScript को कंपाइल किया जाता है. स्क्रिप्ट का आकलन होते समय, उसे गड़बड़ियों के लिए पहले पार्स किया जाता है. अगर पार्सर को गड़बड़ियां नहीं मिलती हैं, तो स्क्रिप्ट को बाइट कोड में कंपाइल किया जाता है और वह एक्ज़ीक्यूशन जारी रख सकता है.
स्क्रिप्ट की जांच करना ज़रूरी होता है, लेकिन इसकी वजह से आपको समस्या हो सकती है. ऐसा इसलिए है, क्योंकि उपयोगकर्ता किसी पेज के रेंडर होने के कुछ समय बाद उसके साथ इंटरैक्ट करने की कोशिश कर सकते हैं. हालांकि, किसी पेज को रेंडर करने का मतलब यह नहीं है कि पेज की लोडिंग पूरी हो गई है. पेज लोड होने के दौरान होने वाले इंटरैक्शन में देरी हो सकती है, क्योंकि पेज स्क्रिप्ट का आकलन करने में व्यस्त होता है. हालांकि, इस बात की कोई गारंटी नहीं है कि अभी इंटरैक्शन हो सकता है. ऐसा इसलिए, क्योंकि हो सकता है कि इसके लिए ज़िम्मेदार स्क्रिप्ट अभी तक लोड न हुई हो. हालांकि, JavaScript के आधार पर इंटरैक्शन हो सकते हैं, जो तैयार हैं या इंटरैक्शन का JavaScript पर कोई असर नहीं पड़ता.
स्क्रिप्ट और उनका आकलन करने वाले टास्क के बीच संबंध
स्क्रिप्ट के आकलन के लिए ज़िम्मेदार टास्क किस तरह से शुरू किए जाएंगे, यह इस बात पर निर्भर करता है कि लोड की जा रही स्क्रिप्ट, किसी सामान्य <script>
एलिमेंट के साथ लोड की गई है या स्क्रिप्ट, type=module
से लोड किया गया मॉड्यूल है. ब्राउज़र चीज़ों को अलग-अलग तरीके से हैंडल करते हैं. इसलिए, बड़े ब्राउज़र इंजन, स्क्रिप्ट जांच को किस तरह हैंडल करते हैं इसका असर, स्क्रिप्ट इवैलुएशन के व्यवहार में होने वाले बदलाव पर होगा.
<script>
एलिमेंट के साथ लोड की गई स्क्रिप्ट
आम तौर पर, स्क्रिप्ट का आकलन करने के लिए भेजे गए टास्क की संख्या का, पेज पर मौजूद <script>
एलिमेंट की संख्या से सीधा संबंध होता है. हर <script>
एलिमेंट, अनुरोध की गई स्क्रिप्ट का आकलन करने के लिए टास्क को शुरू करता है, ताकि उसे पार्स किया जा सके, कंपाइल किया जा सके, और एक्ज़ीक्यूट किया जा सके. यह Chromium-आधारित ब्राउज़र, Safari, और Firefox के मामले में लागू होता है.
यह क्यों मायने रखता है? मान लें कि आपको अपनी प्रोडक्शन स्क्रिप्ट मैनेज करने के लिए बंडलर का इस्तेमाल करना है. साथ ही, आपने इसे इस तरह से कॉन्फ़िगर किया है कि आपके पेज को एक ही स्क्रिप्ट में चलाने के लिए ज़रूरी सभी चीज़ों को बंडल किया जा सके. अगर आपकी वेबसाइट पर भी यही बात लागू होती है, तो उस स्क्रिप्ट का आकलन करने के लिए एक ही टास्क भेजा जाएगा. क्या यह ठीक नहीं है? ऐसा ज़रूरी नहीं है—जब तक कि वह स्क्रिप्ट बड़ी न हो.
JavaScript के बड़े हिस्सों को लोड होने से बचाकर, स्क्रिप्ट की जांच करने के काम को छोटे-छोटे हिस्सों में बांटा जा सकता है. साथ ही, अतिरिक्त <script>
एलिमेंट का इस्तेमाल करके, अलग-अलग छोटी स्क्रिप्ट लोड की जा सकती हैं.
पेज लोड होने के दौरान, आपको हमेशा कम से कम JavaScript लोड करने की कोशिश करनी चाहिए. हालांकि, अपनी स्क्रिप्ट को अलग-अलग हिस्सों में बांटने से यह पक्का होता है कि मुख्य थ्रेड को ब्लॉक करने वाले एक बड़े टास्क के बजाय, आपके पास ज़्यादा से ज़्यादा ऐसे छोटे टास्क हों जो मुख्य थ्रेड को पूरी तरह ब्लॉक न कर पाएं. या कम से कम, आपके शुरू किए गए काम से कम हों.
स्क्रिप्ट की जांच करने के लिए, अलग-अलग टास्क को अलग-अलग किया जा सकता है. यह इंटरैक्शन के दौरान ट्रिगर होने वाले इवेंट कॉलबैक के दौरान मिलने वाले टास्क जैसा होता है. हालांकि, स्क्रिप्ट मूल्यांकन से यील्डिंग प्रणाली आपके लोड की जाने वाली JavaScript को कई छोटी स्क्रिप्ट में बांट देती है, जिससे मुख्य थ्रेड के ब्लॉक होने की संभावना कम हो जाती है.
<script>
एलिमेंट और type=module
एट्रिब्यूट के साथ लोड की गई स्क्रिप्ट
अब <script>
एलिमेंट पर type=module
एट्रिब्यूट के साथ, ब्राउज़र में ES मॉड्यूल को मूल रूप से लोड किया जा सकता है. स्क्रिप्ट लोड करने के इस तरीके से, डेवलपर को मिलने वाले अनुभव के कुछ फ़ायदे मिलते हैं. जैसे, प्रोडक्शन के लिए कोड को बदलने की ज़रूरत नहीं. खास तौर पर, जब इसे इंपोर्ट मैप के साथ इस्तेमाल किया जाता हो. हालांकि, इस तरह से स्क्रिप्ट लोड करने से ऐसे टास्क शेड्यूल हो जाते हैं जो हर ब्राउज़र के लिए अलग-अलग होते हैं.
Chromium पर आधारित ब्राउज़र
Chrome जैसे ब्राउज़र में—या इससे बने ब्राउज़र में—type=module
एट्रिब्यूट का इस्तेमाल करके ES मॉड्यूल को लोड करने से कई तरह के टास्क जनरेट होते हैं, जो आम तौर पर type=module
का इस्तेमाल न करने पर आपको नहीं दिखते. उदाहरण के लिए, हर मॉड्यूल स्क्रिप्ट के लिए एक टास्क चलेगा, जिसमें कंपाइल मॉड्यूल के तौर पर लेबल की गई गतिविधि शामिल होगी.
मॉड्यूल कंपाइल हो जाने के बाद, उनमें चलने वाला कोई भी कोड, मॉड्यूल का आकलन करें के तौर पर लेबल की गई गतिविधि को शुरू कर देगा.
कम से कम, Chrome और उससे जुड़े ब्राउज़र में इसका असर यह है कि ES मॉड्यूल का इस्तेमाल करते समय कंपाइलेशन के चरण पूरे हो गए हैं. लंबे टास्क मैनेज करने के मामले में यह साफ़ तौर पर एक अच्छी बात है; हालांकि, मॉड्यूल का आकलन करने से यह पता चलता है कि आपको कुछ लागत का सामना करना पड़ रहा है, जिसे पूरा नहीं किया जा सकता. हालांकि, आपको कोशिश करनी चाहिए कि ब्राउज़र की परवाह किए बिना, ES मॉड्यूल का इस्तेमाल करके जितना हो सके उतना कम JavaScript भेजें:
- सभी मॉड्यूल कोड, स्ट्रिक्ट मोड में अपने-आप चलते हैं. इसकी मदद से, JavaScript इंजन ऐसे ऑप्टिमाइज़ेशन की अनुमति देते हैं जो बिना किसी सख्त कॉन्टेक्स्ट के नहीं बनाए जा सकते थे.
type=module
का इस्तेमाल करके लोड की जाने वाली स्क्रिप्ट को डिफ़ॉल्ट रूप से स्थगित माना जाता है.type=module
के साथ लोड की गई स्क्रिप्ट पर, इस व्यवहार को बदलने के लिएasync
एट्रिब्यूट का इस्तेमाल किया जा सकता है.
Safari और Firefox
Safari और Firefox में मॉड्यूल लोड होने पर, हर मॉड्यूल का आकलन एक अलग टास्क में किया जाता है. इसका मतलब है कि अन्य मॉड्यूल के लिए, सिर्फ़ स्टैटिक import
स्टेटमेंट वाला एक टॉप-लेवल मॉड्यूल, सैद्धांतिक तौर पर लोड किया जा सकता है. लोड होने वाले हर मॉड्यूल को इसकी जांच करने के लिए, एक अलग नेटवर्क अनुरोध और टास्क देना होगा.
डाइनैमिक import()
के साथ लोड की गई स्क्रिप्ट
स्क्रिप्ट लोड करने का दूसरा तरीका डाइनैमिक import()
है. किसी ES मॉड्यूल के सबसे ऊपर वाले स्टैटिक import
स्टेटमेंट के उलट, मांग पर JavaScript का कुछ हिस्सा लोड करने के लिए डाइनैमिक import()
कॉल, स्क्रिप्ट में कहीं भी दिख सकता है. इस तकनीक को कोड बांटना कहा जाता है.
जब आईएनपी को बेहतर बनाने की बात आती है, तो डाइनैमिक import()
के दो फ़ायदे होते हैं:
- ऐसे मॉड्यूल जो बाद में लोड होने के लिए रोके जाते हैं, स्टार्टअप के दौरान मुख्य थ्रेड के विवाद को कम करते हैं. ऐसा करते समय, उस समय लोड किए गए JavaScript की मात्रा कम हो जाती है. इससे मुख्य थ्रेड खाली हो जाता है, ताकि यह उपयोगकर्ता के इंटरैक्शन के लिए ज़्यादा रिस्पॉन्सिव हो.
- डाइनैमिक
import()
कॉल किए जाने पर, हर कॉल, हर मॉड्यूल के कंपाइलेशन और आकलन को अपने टास्क से अलग करेगा. बेशक, एक बहुत बड़ा मॉड्यूल लोड करने वाला डाइनैमिकimport()
, स्क्रिप्ट की जांच करने वाला एक बड़ा टास्क शुरू करेगा. इसकी वजह से, डाइनैमिकimport()
कॉल के साथ इंटरैक्शन होने पर, मुख्य थ्रेड, उपयोगकर्ता के इनपुट को जवाब नहीं दे पाएगी. इसलिए, यह ज़रूरी है कि आप कम से कम JavaScript लोड करें.
डाइनैमिक import()
कॉल, सभी मुख्य ब्राउज़र इंजन में एक ही तरह से काम करते हैं: स्क्रिप्ट की जांच करने वाले टास्क, डाइनैमिक तौर पर इंपोर्ट किए गए मॉड्यूल की संख्या के बराबर होते हैं.
वेब वर्कर में लोड की गई स्क्रिप्ट
वेब वर्कर, JavaScript इस्तेमाल के एक खास उदाहरण हैं. वेब वर्कर को मुख्य थ्रेड पर रजिस्टर किया जाता है. इसके बाद, वर्कर में मौजूद कोड, अपने थ्रेड पर चलता है. यह इस रूप में बेहद फ़ायदेमंद है कि—वेब वर्कर को रजिस्टर करने वाला कोड, मुख्य थ्रेड पर चलता है, लेकिन वेब वर्कर के अंदर मौजूद कोड काम नहीं करता. इससे मुख्य थ्रेड में कम भीड़ होती है और उपयोगकर्ता के इंटरैक्शन के हिसाब से, मुख्य थ्रेड बेहतर तरीके से काम करती है.
मुख्य थ्रेड के काम को कम करने के अलावा, वेब वर्कर खुद ही वर्कर के कॉन्टेक्स्ट में इस्तेमाल की जाने वाली बाहरी स्क्रिप्ट लोड कर सकते हैं. ऐसा मॉड्यूल वर्कर के साथ काम करने वाले ब्राउज़र में importScripts
या स्टैटिक import
स्टेटमेंट की मदद से किया जा सकता है. इसका नतीजा यह होता है कि जब वेब वर्कर किसी स्क्रिप्ट का अनुरोध करता है, तो उसकी जांच मुख्य थ्रेड से हटाकर की जाती है.
ट्रेड-ऑफ़ और ज़रूरी बातें
जब आपकी स्क्रिप्ट को अलग-अलग हिस्सों में बांटा जाता है, तो छोटी फ़ाइलें लंबे समय तक चलने वाले टास्क को सीमित कर देती हैं. इससे कम या ज़्यादा बड़ी फ़ाइलें लोड होती हैं. स्क्रिप्ट को छोटे-छोटे हिस्सों में बांटने का तरीका तय करते समय कुछ बातों का ध्यान रखना ज़रूरी है.
कंप्रेस करने की क्षमता
जब स्क्रिप्ट को विभाजित करने की बात आती है, तो कंप्रेशन एक कारक होता है. स्क्रिप्ट छोटी होने पर, कंप्रेस करने की सुविधा थोड़ी कम बेहतर हो जाती है. बड़ी स्क्रिप्ट को कंप्रेशन से ज़्यादा फ़ायदा होगा. कंप्रेस करने की क्षमता को बढ़ाने से, स्क्रिप्ट के लोड होने में लगने वाले समय को कम से कम रखने में मदद मिलती है. हालांकि, यह एक संतुलन की तरह काम करती है. इससे यह पक्का किया जाता है कि स्क्रिप्ट को छोटे-छोटे हिस्सों में बांटा जा रहा है, ताकि स्टार्टअप के दौरान बेहतर तरीके से बातचीत की जा सके.
बंडलर, उन स्क्रिप्ट के आउटपुट साइज़ को मैनेज करने के लिए बिलकुल सही होते हैं जिन पर आपकी वेबसाइट निर्भर करती है:
- अगर webpack की समस्या है, तो इसके
SplitChunksPlugin
प्लगिन से आपको मदद मिल सकती है. ऐसेट के साइज़ को मैनेज करने के लिए सेट किए गए विकल्पों के बारे में जानने के लिए,SplitChunksPlugin
का दस्तावेज़ देखें. - Rollup और esbuild जैसे अन्य बंडलर के लिए, अपने कोड में डाइनैमिक
import()
कॉल का इस्तेमाल करके, स्क्रिप्ट फ़ाइल के साइज़ को मैनेज किया जा सकता है. ये बंडलर और वेबपैक—डाइनैमिक तरीके से इंपोर्ट की गई ऐसेट को अपने-आप अलग-अलग फ़ाइल में बांट देंगे, ताकि बंडल का शुरुआती साइज़ बड़ा न हो.
कैश मेमोरी में सेव पेजों का अमान्य होना
कैश मेमोरी में सेव पेजों का अमान्य होना इस बात पर काफ़ी असर डालता है कि किसी पेज पर बार-बार विज़िट करने पर, पेज कितनी तेज़ी से लोड होता है. बड़े, मोनोलिथिक स्क्रिप्ट बंडल भेजने पर, ब्राउज़र कैशिंग के मामले में आपको नुकसान नहीं होता है. ऐसा इसलिए होता है, क्योंकि पैकेज अपडेट करने या शिपिंग की गड़बड़ियां ठीक करने के ज़रिए, पहले पक्ष के कोड अपडेट करने पर पूरा बंडल अमान्य हो जाता है और उसे फिर से डाउनलोड करना पड़ता है.
अपनी स्क्रिप्ट को अलग-अलग करके, आप न सिर्फ़ छोटे-छोटे टास्क के लिए स्क्रिप्ट का आकलन करते हैं, बल्कि इससे आपकी साइट पर वापस आने वाले लोगों की, नेटवर्क के बजाय ब्राउज़र कैश से ज़्यादा स्क्रिप्ट पाने की संभावना भी बढ़ जाती है. इससे पेज तेज़ी से लोड होता है.
नेस्ट किए गए मॉड्यूल और लोड होने की परफ़ॉर्मेंस
अगर प्रोडक्शन में ES मॉड्यूल भेजे जा रहे हैं और उन्हें type=module
एट्रिब्यूट के साथ लोड किया जा रहा है, तो आपको इस बात की जानकारी होनी चाहिए कि मॉड्यूल नेस्टिंग, स्टार्टअप समय पर कैसे असर डाल सकती है. मॉड्यूल नेस्टिंग का मतलब है कि जब कोई ES मॉड्यूल स्टैटिक रूप से कोई दूसरा ES मॉड्यूल इंपोर्ट करता है, जो स्टैटिक तरीके से किसी दूसरे ES मॉड्यूल को इंपोर्ट करता है:
// a.js
import {b} from './b.js';
// b.js
import {c} from './c.js';
अगर आपके ES मॉड्यूल एक साथ बंडल नहीं किए गए हैं, तो पहले वाले कोड से नेटवर्क अनुरोध चेन बनती है: जब <script>
एलिमेंट से a.js
का अनुरोध किया जाता है, तो b.js
के लिए दूसरा नेटवर्क अनुरोध भेजा जाता है. इसके बाद, c.js
के लिए एक और अनुरोध भेजा जाता है. इससे बचने का एक तरीका है बंडलर का इस्तेमाल करना—लेकिन यह पक्का कर लें कि आप अपने बंडलर को स्क्रिप्ट के अलग-अलग हिस्सों को इस तरह से कॉन्फ़िगर कर रहे हैं जिससे स्क्रिप्ट का आकलन करने वाले काम को अलग-अलग तरीके से किया जा सके.
अगर आपको बंडलर का इस्तेमाल नहीं करना है, तो नेस्ट किए गए मॉड्यूल कॉल से बचने का एक और तरीका modulepreload
संसाधन संकेत का इस्तेमाल करना है. यह ES मॉड्यूल को समय से पहले लोड कर देगा, ताकि नेटवर्क के अनुरोध की चेन से बचा जा सके.
नतीजा
ब्राउज़र में स्क्रिप्ट का मूल्यांकन करके उन्हें ऑप्टिमाइज़ करना कोई बड़ी चुनौती है. यह तरीका आपकी वेबसाइट की ज़रूरी शर्तों और शर्तों पर निर्भर करता है. हालांकि, स्क्रिप्ट को बांटने से, स्क्रिप्ट की जांच करने का काम कई छोटे-छोटे टास्क पर हो जाता है. इससे, मुख्य थ्रेड को ब्लॉक करने के बजाय, मुख्य थ्रेड को उपयोगकर्ता के इंटरैक्शन को बेहतर तरीके से मैनेज करने की सुविधा मिलती है.
आपको याद दिला दें कि स्क्रिप्ट की जांच करने वाले बड़े टास्क को अलग-अलग करने के लिए, यहां कुछ तरीके बताए गए हैं:
type=module
एट्रिब्यूट के बिना<script>
एलिमेंट का इस्तेमाल करके स्क्रिप्ट लोड करते समय, बहुत बड़ी स्क्रिप्ट लोड करने से बचें. इनकी वजह से, स्क्रिप्ट का आकलन करने वाले ऐसे टास्क शुरू हो जाते हैं जिनमें बहुत ज़्यादा संसाधन होते हैं. ये टास्क मुख्य थ्रेड को ब्लॉक करते हैं. इस काम को अलग-अलग करने के लिए, अपनी स्क्रिप्ट को ज़्यादा<script>
एलिमेंट में फैलाएं.- ब्राउज़र में ES मॉड्यूल को मूल रूप से लोड करने के लिए,
type=module
एट्रिब्यूट का इस्तेमाल करने पर, हर अलग मॉड्यूल स्क्रिप्ट का आकलन करने के लिए, अलग-अलग टास्क शुरू हो जाएंगे. - डाइनैमिक
import()
कॉल का इस्तेमाल करके, अपने शुरुआती बंडल का साइज़ कम करें. यह बंडलर में भी काम करता है, क्योंकि बंडलर डाइनैमिक तरीके से इंपोर्ट किए गए हर मॉड्यूल को "स्प्लिट पॉइंट" के तौर पर ट्रीट करेगा. जिससे हर डाइनैमिक रूप से इंपोर्ट किए गए मॉड्यूल के लिए एक अलग स्क्रिप्ट जनरेट होती है. - कंप्रेशन की क्षमता और कैश मेमोरी में सेव अमान्य होने जैसी समस्याओं को ज़रूर ध्यान में रखें. बड़ी स्क्रिप्ट बेहतर तरीके से कंप्रेस हो जाएंगी. हालांकि, इनमें कम कामों में ही स्क्रिप्ट का आकलन करने में ज़्यादा खर्चा होने की संभावना ज़्यादा होती है. इसकी वजह से, ब्राउज़र की कैश मेमोरी अमान्य हो सकती है. इस वजह से, कैश मेमोरी में डेटा सेव करने की क्षमता कम हो जाती है.
- अगर बंडल किए बिना ईएस मॉड्यूल का इस्तेमाल किया जा रहा है, तो स्टार्टअप के दौरान लोड होने की प्रोसेस को ऑप्टिमाइज़ करने के लिए,
modulepreload
संसाधन संकेत का इस्तेमाल करें. - हमेशा की तरह, कम से कम JavaScript की शिपिंग करें.
यह एक संतुलन बनाने वाला काम है, लेकिन स्क्रिप्ट को अलग करके और डाइनैमिक import()
की मदद से, शुरुआती पेलोड को कम करके, स्टार्टअप के परफ़ॉर्मेंस को बेहतर बनाया जा सकता है. साथ ही, स्टार्टअप के दौरान उपयोगकर्ताओं के इंटरैक्शन को भी आसानी से अडजस्ट किया जा सकता है. इससे आपको आईएनपी मेट्रिक में बेहतर स्कोर करने में मदद मिलेगी, जिससे उपयोगकर्ता अनुभव बेहतर होगा.
मार्कस स्पाइस्क की किताब Unस्प्लैश की हीरो इमेज.