स्क्रिप्ट लोड करते समय, ब्राउज़र को उन्हें लागू करने से पहले उनका आकलन करने में समय लगता है. इस वजह से, टास्क पूरा होने में ज़्यादा समय लग सकता है. जानें कि स्क्रिप्ट का आकलन कैसे किया जाता है. साथ ही, यह भी जानें कि पेज लोड होने के दौरान, लंबे समय तक चलने वाले टास्क से बचने के लिए क्या किया जा सकता है.
इंटरैक्शन टू नेक्स्ट पेंट (आईएनपी) को ऑप्टिमाइज़ करने के लिए, आपको इंटरैक्शन को खुद ऑप्टिमाइज़ करने की सलाह मिलेगी. उदाहरण के लिए, लंबे टास्क को ऑप्टिमाइज़ करने की गाइड में, setTimeout
के साथ येल्ड करने जैसी तकनीकों के बारे में बताया गया है. ये तकनीकें फ़ायदेमंद होती हैं, क्योंकि इनसे मुख्य थ्रेड को लंबे टास्क से बचने में मदद मिलती है. इससे इंटरैक्शन और अन्य गतिविधियों को जल्दी चलाने के ज़्यादा अवसर मिलते हैं. ऐसा तब नहीं होता, जब उन्हें किसी एक लंबे टास्क के लिए इंतज़ार करना पड़ता है.
हालांकि, स्क्रिप्ट लोड करने से जुड़े लंबे टास्क के बारे में क्या? इन टास्क से, उपयोगकर्ता के इंटरैक्शन में रुकावट आ सकती है. साथ ही, पेज के लोड होने के दौरान उसके INP पर असर पड़ सकता है. इस गाइड में बताया गया है कि स्क्रिप्ट के आकलन से शुरू होने वाले टास्क को ब्राउज़र कैसे मैनेज करते हैं. साथ ही, यह भी बताया गया है कि स्क्रिप्ट के आकलन के काम को अलग-अलग करने के लिए क्या किया जा सकता है, ताकि पेज लोड होने के दौरान आपकी मुख्य थ्रेड, उपयोगकर्ता के इनपुट के लिए ज़्यादा रिस्पॉन्सिव हो सके.
स्क्रिप्ट का आकलन क्या है?
अगर आपने किसी ऐसे ऐप्लिकेशन की प्रोफ़ाइल बनाई है जो बहुत ज़्यादा 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 मॉड्यूल का इस्तेमाल करते समय कंपाइल करने के चरणों को अलग-अलग कर दिया जाता है. लंबे टास्क मैनेज करने के मामले में, यह एक बेहतर विकल्प है. हालांकि, मॉड्यूल के आकलन के बाद, आपको कुछ खर्च करना पड़ सकता है. आपको ज़्यादा से ज़्यादा JavaScript शिप करने की कोशिश करनी चाहिए. हालांकि, किसी भी ब्राउज़र में ES मॉड्यूल का इस्तेमाल करने से ये फ़ायदे मिलते हैं:
- सभी मॉड्यूल कोड, स्ट्रिक्ट मोड में अपने-आप चलता है. इससे JavaScript इंजन, ऐसे ऑप्टिमाइज़ेशन कर पाते हैं जो स्ट्रिक्ट मोड के अलावा किसी दूसरे मोड में नहीं किए जा सकते.
type=module
का इस्तेमाल करके लोड की गई स्क्रिप्ट को डिफ़ॉल्ट रूप से देर से लोड होने वाली स्क्रिप्ट माना जाता है. इस व्यवहार को बदलने के लिए,type=module
के साथ लोड की गई स्क्रिप्ट परasync
एट्रिब्यूट का इस्तेमाल किया जा सकता है.
Safari और Firefox
जब Safari और Firefox में मॉड्यूल लोड किए जाते हैं, तो हर मॉड्यूल का आकलन अलग-अलग टास्क में किया जाता है. इसका मतलब है कि आप सैद्धांतिक तौर पर, दूसरे मॉड्यूल के लिए सिर्फ़ स्टैटिक import
स्टेटमेंट वाला एक टॉप-लेवल मॉड्यूल लोड कर सकते हैं. साथ ही, लोड होने वाले हर मॉड्यूल को इसकी जांच करने के लिए एक अलग नेटवर्क अनुरोध और टास्क देना होगा.
डाइनैमिक import()
की मदद से लोड की गई स्क्रिप्ट
स्क्रिप्ट लोड करने का एक और तरीका, डाइनैमिक import()
है. किसी ES मॉड्यूल के सबसे ऊपर वाले स्टैटिक import
स्टेटमेंट के उलट, मांग पर JavaScript का कुछ हिस्सा लोड करने के लिए डाइनैमिक import()
कॉल, स्क्रिप्ट में कहीं भी दिख सकता है. इस तकनीक को कोड को अलग-अलग हिस्सों में बांटना कहा जाता है.
डाइनैमिक import()
का इस्तेमाल करने पर, INP को बेहतर बनाने के दो फ़ायदे मिलते हैं:
- जिन मॉड्यूल को बाद में लोड करने के लिए टाला जाता है वे स्टार्टअप के दौरान मुख्य थ्रेड के कॉन्टेंट को कम करते हैं. ऐसा, उस समय लोड किए गए 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()
कॉल का इस्तेमाल करके, अपने शुरुआती बंडल का साइज़ कम करें. यह सुविधा बंडलर में भी काम करती है, क्योंकि बंडलर हर डाइनैमिक तौर पर इंपोर्ट किए गए मॉड्यूल को "स्प्लिट पॉइंट" के तौर पर इस्तेमाल करते हैं. इस वजह से, हर डाइनैमिक तौर पर इंपोर्ट किए गए मॉड्यूल के लिए एक अलग स्क्रिप्ट जनरेट की जाती है. - कॉम्प्रेस करने की सुविधा की परफ़ॉर्मेंस और कैश मेमोरी को अमान्य करने जैसे फ़ायदों और नुकसानों को ध्यान में रखें. बड़ी स्क्रिप्ट को बेहतर तरीके से कंप्रेस किया जा सकता है. हालांकि, कम टास्क में स्क्रिप्ट की जांच करने के लिए ज़्यादा खर्च की संभावना होती है. साथ ही, ब्राउज़र कैश मेमोरी अमान्य हो जाती है. इस वजह से, कैश मेमोरी का इस्तेमाल कम होता है.
- अगर ES मॉड्यूल को बंडल किए बिना नेटिव तौर पर इस्तेमाल किया जा रहा है, तो स्टार्टअप के दौरान उनके लोड होने को ऑप्टिमाइज़ करने के लिए,
modulepreload
संसाधन के संकेत का इस्तेमाल करें. - हमेशा की तरह, जितना हो सके उतना कम JavaScript शिप करें.
यह एक संतुलन बनाने वाला काम है, लेकिन स्क्रिप्ट को अलग करके और डाइनैमिक import()
की मदद से, शुरुआती पेलोड को कम करके, स्टार्टअप के परफ़ॉर्मेंस को बेहतर बनाया जा सकता है. साथ ही, स्टार्टअप के दौरान उपयोगकर्ताओं के इंटरैक्शन को भी आसानी से अडजस्ट किया जा सकता है. इससे आपको आईएनपी मेट्रिक में बेहतर स्कोर करने में मदद मिलेगी, जिससे उपयोगकर्ता अनुभव बेहतर होगा.