परिचय
इस लेख में, हम आपको ब्राउज़र में कुछ JavaScript लोड करने और उसे लागू करने का तरीका बताएंगे.
नहीं, रुकिए, वापस आएं! हम जानते हैं कि यह सामान्य और आसान लगता है, लेकिन याद रखें कि यह ब्राउज़र में हो रहा है. यहां सिद्धांत के हिसाब से आसान काम, लेगसी की वजह से मुश्किल हो जाता है. इन समस्याओं के बारे में जानने से, स्क्रिप्ट लोड करने का सबसे तेज़ और कम रुकावट वाला तरीका चुना जा सकता है. अगर आपका शेड्यूल तय है, तो क्विक रेफ़रंस पर जाएं.
शुरुआत करने के लिए, यहां बताया गया है कि स्पेसिफ़िकेशन में, स्क्रिप्ट को डाउनलोड और लागू करने के अलग-अलग तरीकों के बारे में कैसे बताया गया है:
WHATWG के सभी स्पेसिफ़िकेशन की तरह, यह शुरुआत में स्क्रैबल फ़ैक्ट्री में क्लस्टर बम के बाद की स्थिति जैसा दिखता है. हालांकि, इसे पांचवीं बार पढ़ने और अपनी आंखों से खून पोंछने के बाद, यह वाकई में काफ़ी दिलचस्प लगता है:
मेरी पहली स्क्रिप्ट में
<script src="//other-domain.com/1.js"></script>
<script src="2.js"></script>
वाह, कितनी आसानी से हो गया. यहां ब्राउज़र, दोनों स्क्रिप्ट को एक साथ डाउनलोड करेगा और उन्हें जल्द से जल्द लागू करेगा. साथ ही, उनके क्रम को भी बनाए रखेगा. “2.js” तब तक लागू नहीं होगा, जब तक “1.js” लागू नहीं हो जाता (या लागू नहीं हो पाता). इसी तरह, “1.js” तब तक लागू नहीं होगा, जब तक पिछली स्क्रिप्ट या स्टाइलशीट लागू नहीं हो जाती.
माफ़ करें, ऐसा होने पर ब्राउज़र, पेज को रेंडर करने की प्रोसेस को रोक देता है. ऐसा “वेब के शुरुआती दौर” के DOM API की वजह से होता है. इनकी मदद से, उस कॉन्टेंट में स्ट्रिंग जोड़ी जा सकती है जिसे पार्स किया जा रहा है. जैसे, document.write
. नए ब्राउज़र, बैकग्राउंड में दस्तावेज़ को स्कैन या पार्स करना जारी रखेंगे. साथ ही, बाहरी कॉन्टेंट (js, इमेज, सीएसएस वगैरह) के लिए डाउनलोड ट्रिगर करेंगे, लेकिन रेंडरिंग अब भी ब्लॉक है.
इसलिए, अच्छी और बेहतरीन परफ़ॉर्मेंस वाली दुनिया की यह सलाह है कि आप अपने दस्तावेज़ के आखिर में स्क्रिप्ट एलिमेंट का इस्तेमाल करें. ऐसा इसलिए, क्योंकि इससे बहुत कम कॉन्टेंट ब्लॉक होता है. माफ़ करें, इसका मतलब है कि ब्राउज़र तब तक आपकी स्क्रिप्ट नहीं देखता, जब तक वह आपका पूरा एचटीएमएल डाउनलोड नहीं कर लेता. इस दौरान, वह सीएसएस, इमेज, और iframe जैसे अन्य कॉन्टेंट को डाउनलोड करना शुरू कर देता है. आधुनिक ब्राउज़र, इमेज के बजाय JavaScript को प्राथमिकता देने के लिए स्मार्ट हैं, लेकिन हम इससे बेहतर कर सकते हैं.
धन्यवाद आईई! (नहीं, मैं व्यंग्य नहीं कर रहा/रही)
<script src="//other-domain.com/1.js" defer></script>
<script src="2.js" defer></script>
Microsoft ने परफ़ॉर्मेंस की इन समस्याओं को पहचाना और Internet Explorer 4 में "टाला" दिया. इसका मतलब है कि “मैं वादा करता/करती हूं कि document.write
जैसी चीज़ों का इस्तेमाल करके, पार्स करने वाले टूल में कोई चीज़ इंजेक्ट नहीं की जाएगी. अगर मैं यह वादा नहीं निभाता, तो मुझे अपनी पसंद के मुताबिक सज़ा दी जा सकती है”. इस एट्रिब्यूट को एचटीएमएल 4 में शामिल किया गया और यह अन्य ब्राउज़र में भी दिखने लगा.
ऊपर दिए गए उदाहरण में, ब्राउज़र दोनों स्क्रिप्ट को साथ-साथ डाउनलोड करेगा और DOMContentLoaded
के फ़ायर होने से ठीक पहले, उनका क्रम बनाए रखेगा.
भेड़ की फ़ैक्ट्री में क्लस्टर बम की तरह, “देर” शब्द का इस्तेमाल गलत जगहों पर होने लगा. “src” और “defer” एट्रिब्यूट के बीच, और स्क्रिप्ट टैग बनाम डाइनैमिक तौर पर जोड़ी गई स्क्रिप्ट के बीच, हमारे पास स्क्रिप्ट जोड़ने के छह पैटर्न हैं. बेशक, ब्राउज़र उस क्रम पर सहमत नहीं थे जिस क्रम में उन्हें काम करना चाहिए. Mozilla ने इस समस्या के बारे में एक बेहतरीन लेख लिखा है, जो 2009 में लिखा गया था.
WHATWG ने इस व्यवहार के बारे में साफ़ तौर पर बताया है. इसमें कहा गया है कि “defer” का उन स्क्रिप्ट पर कोई असर नहीं पड़ता जिन्हें डाइनैमिक तौर पर जोड़ा गया है या जिनमें “src” मौजूद नहीं है. अगर ऐसा नहीं होता है, तो दस्तावेज़ को पार्स करने के बाद, उन स्क्रिप्ट को उसी क्रम में चलाया जाना चाहिए जिस क्रम में उन्हें जोड़ा गया था.
धन्यवाद IE! (ठीक है, अब मैं व्यंग्य कर रहा हूं)
यह देता है, यह लेता है. माफ़ करें, IE4-9 में एक गड़बड़ी है, जिसकी वजह से स्क्रिप्ट अनचाहे क्रम में चल सकती हैं. यहां बताया गया है कि क्या होता है:
1.जेएस
console.log('1');
document.getElementsByTagName('p')[0].innerHTML = 'Changing some content';
console.log('2');
2.js
console.log('3');
मान लें कि पेज पर एक पैराग्राफ है, तो लॉग का अनुमानित क्रम [1, 2, 3] होगा. हालांकि, IE9 और उससे पहले के वर्शन में आपको [1, 3, 2] दिखेगा. डीओएम के कुछ खास ऑपरेशन की वजह से, IE मौजूदा स्क्रिप्ट को रोक देता है और जारी रखने से पहले, बाकी स्क्रिप्ट को चला देता है.
हालांकि, IE10 और अन्य ब्राउज़र जैसे बिना गड़बड़ी वाले वर्शन में भी, स्क्रिप्ट को तब तक लागू नहीं किया जा सकता, जब तक कि पूरा दस्तावेज़ डाउनलोड और पार्स नहीं हो जाता. अगर आपको फिर भी DOMContentLoaded
का इंतज़ार करना है, तो यह तरीका आपके लिए आसान हो सकता है. हालांकि, अगर आपको बेहतर परफ़ॉर्मेंस के लिए ज़्यादा रुझान चाहिए, तो लिसनर और बूटस्ट्रैपिंग को जल्द से जल्द शुरू करें...
HTML5 की मदद से
<script src="//other-domain.com/1.js" async></script>
<script src="2.js" async></script>
HTML5 ने हमें एक नया एट्रिब्यूट, “async” दिया है. इससे यह माना जाता है कि आप document.write
का इस्तेमाल नहीं करेंगे. हालांकि, यह तब तक इंतज़ार नहीं करता, जब तक दस्तावेज़ को पार्स करके उसे लागू नहीं कर दिया जाता. ब्राउज़र, दोनों स्क्रिप्ट को एक साथ डाउनलोड करेगा और उन्हें जल्द से जल्द लागू करेगा.
दरअसल, वे जल्द से जल्द एक्ज़ीक्यूट हो जाएंगे, इसलिए “2.js”, “1.js” से पहले एक्ज़ीक्यूट हो सकता है. स्वतंत्र होने पर “1.js” एक ट्रैकिंग स्क्रिप्ट हो सकती है, जिसका “2.js” से कोई लेना-देना नहीं है. हालांकि, अगर आपका “1.js” jQuery की ऐसी सीडीएन कॉपी है, जिस पर “2.js” इस पर निर्भर करता है कि “2.js” इस पर निर्भर है, तो यह एक क्लस्टर में कोड नहीं होगा...
मुझे पता है कि हमें क्या चाहिए, एक JavaScript लाइब्रेरी!
सबसे बेहतर बात यह है कि रेंडरिंग को ब्लॉक किए बिना, स्क्रिप्ट का सेट तुरंत डाउनलोड हो जाए और जोड़े गए क्रम में, जल्द से जल्द लागू हो जाए. माफ़ करें, एचटीएमएल आपसे नफ़रत करता है और आपको ऐसा करने की अनुमति नहीं देगा.
इस समस्या को JavaScript ने कुछ तरीकों से हल किया. कुछ मामलों में, आपको अपने JavaScript में बदलाव करने पड़ते थे. इसके लिए, आपको उसे किसी ऐसे कॉलबैक में रैप करना होता था जिसे लाइब्रेरी सही क्रम में कॉल करती है. जैसे, RequireJS. कुछ लोग, एक साथ डाउनलोड करने के लिए XHR का इस्तेमाल करते थे. इसके बाद, सही क्रम में eval()
का इस्तेमाल करते थे. यह तरीका, किसी दूसरे डोमेन की स्क्रिप्ट के लिए तब तक काम नहीं करता, जब तक उनमें सीओआरएस हेडर न हो और ब्राउज़र उससे काम न करता हो. कुछ लोगों ने LabJS जैसी सुपर-मैजिक हैक का भी इस्तेमाल किया.
हैकिंग में ब्राउज़र को चकमा दिया जाता है कि वह संसाधन को इस तरह डाउनलोड करे कि वह किसी इवेंट को पूरा होने पर ट्रिगर करे, लेकिन वह उसे न चलाए. LabJS में, स्क्रिप्ट को गलत माइम टाइप के साथ जोड़ा जाएगा, जैसे कि <script type="script/cache" src="...">
. सभी स्क्रिप्ट डाउनलोड होने के बाद, उन्हें सही टाइप के साथ फिर से जोड़ दिया जाएगा. ऐसा करने से, ब्राउज़र उन्हें सीधे कैश मेमोरी से पा सकेगा और उन्हें क्रम से तुरंत लागू कर सकेगा. यह सुविधाजनक लेकिन अनिर्दिष्ट व्यवहार पर निर्भर करता है और यह उस समय गड़बड़ी करता है जब HTML5 द्वारा घोषित ब्राउज़र को किसी ऐसे स्क्रिप्ट डाउनलोड नहीं करने चाहिए, जिसे पहचाना न जा सके. ध्यान दें कि LabJS ने इन बदलावों को अपना लिया है और अब इस लेख में बताए गए तरीकों का इस्तेमाल करता है.
हालांकि, स्क्रिप्ट लोडर की परफ़ॉर्मेंस में भी समस्याएं आती हैं. लाइब्रेरी के JavaScript के डाउनलोड और पार्स होने का इंतज़ार करने के बाद ही, उसमें मैनेज की जा रही किसी भी स्क्रिप्ट को डाउनलोड किया जा सकता है. साथ ही, हम स्क्रिप्ट लोडर को कैसे लोड करेंगे? हम उस स्क्रिप्ट को कैसे लोड करेंगे जो स्क्रिप्ट लोडर को बताती है कि उसे क्या लोड करना है? वॉचमैन को कौन देखता है? मैं नग्न क्यों हूं? ये सभी मुश्किल सवाल हैं.
अगर आपको अन्य स्क्रिप्ट डाउनलोड करने से पहले, कोई अतिरिक्त स्क्रिप्ट फ़ाइल डाउनलोड करनी पड़ती है, तो इसका मतलब है कि आपने परफ़ॉर्मेंस की लड़ाई पहले ही हार दी है.
बचाव के लिए DOM
इसका जवाब, असल में HTML5 स्पेसिफ़िकेशन में है. हालांकि, यह स्क्रिप्ट-लोडिंग सेक्शन में सबसे नीचे छिपा हुआ है.
आइए, इसका अनुवाद “Earthling” में करते हैं:
[
'//other-domain.com/1.js',
'2.js'
].forEach(function(src) {
var script = document.createElement('script');
script.src = src;
document.head.appendChild(script);
});
डाइनैमिक रूप से बनाई गई और दस्तावेज़ में जोड़ी गई स्क्रिप्ट, डिफ़ॉल्ट रूप से एक साथ काम नहीं करतीं. वे रेंडरिंग को ब्लॉक नहीं करती. ऐप्लिकेशन डाउनलोड होते ही उन्हें एक्ज़ीक्यूट नहीं किया जाता. इसका मतलब है कि वे गलत क्रम में दिख सकती हैं. हालांकि, हम उन्हें साफ़ तौर पर 'असाइनमेंट सिंक नहीं होने वाला' के तौर पर मार्क कर सकते हैं:
[
'//other-domain.com/1.js',
'2.js'
].forEach(function(src) {
var script = document.createElement('script');
script.src = src;
script.async = false;
document.head.appendChild(script);
});
इससे हमारी स्क्रिप्ट को अलग-अलग तरह के काम करने की सुविधा मिलती है. यह सुविधा, सादे एचटीएमएल से नहीं मिल सकती. साफ़ तौर पर एक साथ काम नहीं करने की वजह से, स्क्रिप्ट एक्ज़ीक्यूशन लिस्ट में जोड़ी जाती हैं. इसी सूची में वे सादे एचटीएमएल वाले पहले उदाहरण में जोड़ी गई होती हैं. हालांकि, डाइनैमिक तौर पर बनने की वजह से, इन्हें दस्तावेज़ को पार्स करने के बाहर चलाया जाता है. इसलिए, इन्हें डाउनलोड करने के दौरान रेंडरिंग ब्लॉक नहीं होती. सिंक XHR के साथ, नॉन-ऐसिंक स्क्रिप्ट लोडिंग को न जोड़ें. यह कभी भी अच्छी बात नहीं होती.
ऊपर दी गई स्क्रिप्ट को पेजों के हेडर में इनलाइन शामिल किया जाना चाहिए. इससे, प्रोग्रेसिव रेंडरिंग में रुकावट आए बिना, स्क्रिप्ट डाउनलोड की सूची में जितनी जल्दी हो सके उतनी जल्दी शामिल हो जाती है. साथ ही, आपके बताए गए क्रम में जितनी जल्दी हो सके उतनी जल्दी लागू हो जाती है. “1.js” से पहले “2.js” को डाउनलोड किया जा सकता है. हालांकि, इसे तब तक लागू नहीं किया जाएगा, जब तक “1.js” डाउनलोड और लागू नहीं हो जाता या दोनों में से कोई भी काम नहीं करता. वाह! एसिंक्रोनस-डाउनलोड, लेकिन ऑर्डर-एग्ज़ीक्यूशन!
स्क्रिप्ट को इस तरह से लोड करने में, एसिंक्रोनस एट्रिब्यूट के साथ काम करने वाली हर चीज़ का इस्तेमाल किया जा सकता है. हालांकि, Safari 5.0 (5.1 ठीक है) को छोड़कर. इसके अलावा, Firefox और Opera के सभी वर्शन ऐसे वर्शन के रूप में काम करते हैं जो एक साथ काम नहीं करने वाली विशेषता का इस्तेमाल नहीं करते. डाइनैमिक रूप से जोड़ी गई स्क्रिप्ट को उसी क्रम में आसानी से एक्ज़ीक्यूट किया जा सकता है जिस क्रम में वे दस्तावेज़ में जोड़ दिए जाते हैं.
क्या स्क्रिप्ट लोड करने का यह सबसे तेज़ तरीका है? ठीक है?
अगर डाइनैमिक तौर पर यह तय किया जा रहा है कि कौनसी स्क्रिप्ट लोड करनी हैं, तो हां. अगर ऐसा नहीं है, तो शायद नहीं. ऊपर दिए गए उदाहरण में, ब्राउज़र को यह पता लगाने के लिए स्क्रिप्ट को पार्स और लागू करना होगा कि कौनसी स्क्रिप्ट डाउनलोड करनी हैं. इससे, आपकी स्क्रिप्ट, प्रीलोड स्कैनर से छिप जाती हैं. ब्राउज़र इन स्कैनर का इस्तेमाल करके, उन पेजों के रिसॉर्स ढूंढते हैं जिन पर आपके अगले विज़िट करने की संभावना है. इसके अलावा, जब किसी दूसरे रिसॉर्स की वजह से पार्सर ब्लॉक हो जाता है, तब भी ब्राउज़र पेज के रिसॉर्स ढूंढते हैं.
दस्तावेज़ के हेड में यह जानकारी जोड़कर, प्रॉडक्ट को खोजे जाने की सुविधा को फिर से जोड़ा जा सकता है:
<link rel="subresource" href="//other-domain.com/1.js">
<link rel="subresource" href="2.js">
इससे ब्राउज़र को पता चलता है कि पेज को 1.js और 2.js की ज़रूरत है. link[rel=subresource]
, link[rel=prefetch]
से मिलता-जुलता है, लेकिन इसमें अलग सेमेटिक हैं. फ़िलहाल, यह सुविधा सिर्फ़ Chrome पर काम करती है. साथ ही, आपको यह बताना होगा कि कौनसी स्क्रिप्ट दो बार लोड होनी चाहिए. एक बार लिंक एलिमेंट के ज़रिए और दूसरी बार अपनी स्क्रिप्ट में.
सुधार: मैंने पहले बताया था कि इन्हें प्रीलोड स्कैनर ने पिक किया था. ऐसा नहीं है. इन्हें रेगुलर पार्सर ने पिक किया है. हालांकि, प्रीलोड स्कैनर इन स्क्रिप्ट को पिक अप कर सकता है, लेकिन फ़िलहाल ऐसा नहीं होता. वहीं, एक्सीक्यूटेबल कोड में शामिल स्क्रिप्ट को कभी भी प्रीलोड नहीं किया जा सकता. टिप्पणियों में मुझे सही जानकारी देने वाले Yoav Weiss का धन्यवाद.
मुझे यह लेख निराशाजनक लगता है
स्थिति बहुत खराब है और आपको परेशान होना चाहिए. स्क्रिप्ट को तेज़ी से और अलग-अलग क्रम में डाउनलोड करने का कोई ऐसा तरीका नहीं है जो दोहराए जाने वाले निर्देशों के बिना काम करे. HTTP2/SPDY की मदद से, अनुरोध के ओवरहेड को इस हद तक कम किया जा सकता है कि अलग-अलग कैश मेमोरी में सेव की जा सकने वाली कई छोटी फ़ाइलों में स्क्रिप्ट डिलीवर करना सबसे तेज़ तरीका हो सकता है. कल्पना करें:
<script src="dependencies.js"></script>
<script src="enhancement-1.js"></script>
<script src="enhancement-2.js"></script>
<script src="enhancement-3.js"></script>
…
<script src="enhancement-10.js"></script>
हर बेहतर बनाने वाली स्क्रिप्ट, किसी खास पेज कॉम्पोनेंट से जुड़ी होती है. हालांकि, इसके लिए dependencies.js में यूटिलिटी फ़ंक्शन की ज़रूरत होती है. हमारा सुझाव है कि सभी स्क्रिप्ट को एसिंक्रोनस तरीके से डाउनलोड करें. इसके बाद, बेहतर बनाने वाली स्क्रिप्ट को जल्द से जल्द किसी भी क्रम में, लेकिन dependencies.js के बाद लागू करें. यह प्रोग्रेसिव प्रोग्रेसिव एन्हैंसमेंट है! माफ़ करें, ऐसा करने का कोई तरीका नहीं है. ऐसा तब तक नहीं किया जा सकता, जब तक dependencies.js की लोडिंग स्थिति को ट्रैक करने के लिए, स्क्रिप्ट में बदलाव न किया जाए. async=false से भी इस समस्या का समाधान नहीं होता है, क्योंकि एन्हैंसमेंट-10.js का इस्तेमाल, 1-9 तक ब्लॉक हो जाएगा. असल में, सिर्फ़ एक ब्राउज़र है जो हैक के बिना ऐसा करने की सुविधा देता है…
IE को एक आइडिया आया है!
IE, स्क्रिप्ट को दूसरे ब्राउज़र से अलग तरीके से लोड करता है.
var script = document.createElement('script');
script.src = 'whatever.js';
IE, “whatever.js” को डाउनलोड करना शुरू कर देता है. हालांकि, अन्य ब्राउज़र तब तक डाउनलोड नहीं करते, जब तक दस्तावेज़ में स्क्रिप्ट नहीं जोड़ी जाती. IE में एक इवेंट, “readystatechange” और प्रॉपर्टी, “readystate” भी होती है. इससे हमें लोडिंग की प्रोग्रेस के बारे में पता चलता है. यह सुविधा वाकई बहुत काम की है, क्योंकि इससे हमें स्क्रिप्ट को लोड करने और उन्हें अलग-अलग तरीके से लागू करने का कंट्रोल मिलता है.
var script = document.createElement('script');
script.onreadystatechange = function() {
if (script.readyState == 'loaded') {
// Our script has download, but hasn't executed.
// It won't execute until we do:
document.body.appendChild(script);
}
};
script.src = 'whatever.js';
हम दस्तावेज़ में स्क्रिप्ट जोड़ने का समय चुनकर, जटिल डिपेंडेंसी मॉडल बना सकते हैं. IE के 6वें वर्शन से, इस मॉडल का इस्तेमाल किया जा सकता है. यह काफ़ी दिलचस्प है, लेकिन इसमें अब भी async=false
की तरह ही, प्रीलोडर को खोजने से जुड़ी समस्या है.
बस हो गया! मैं स्क्रिप्ट कैसे लोड करूं?
ठीक है. अगर आपको स्क्रिप्ट को इस तरह से लोड करना है कि वह रेंडरिंग को ब्लॉक न करे, बार-बार लोड न हो, और ब्राउज़र के साथ बेहतर तरीके से काम करे, तो हमारा सुझाव है कि:
<script src="//other-domain.com/1.js"></script>
<script src="2.js"></script>
वह. बॉडी एलिमेंट के आखिर में. हां, वेब डेवलपर होना काफ़ी हद तक किंग सिसिफ़स (बूम! ग्रीक पौराणिक कथाओं के रेफ़रंस के लिए 100 हिप्स्टर पॉइंट!). एचटीएमएल और ब्राउज़र की सीमाओं की वजह से, हम ज़्यादा बेहतर तरीके से काम नहीं कर पाते.
मुझे उम्मीद है कि JavaScript मॉड्यूल, स्क्रिप्ट लोड करने के लिए बिना ब्लॉक किए, एलान करने वाला तरीका उपलब्ध कराकर हमारी मदद करेंगे. साथ ही, स्क्रिप्ट को लागू करने के क्रम को कंट्रोल करने में भी मदद करेंगे. हालांकि, इसके लिए स्क्रिप्ट को मॉड्यूल के तौर पर लिखना ज़रूरी है.
ओह, कोई ऐसा बेहतर सुझाव तो होगा जिसे हम अब इस्तेमाल कर सकें?
अगर आपको परफ़ॉर्मेंस को बेहतर बनाने के लिए, ऊपर बताई गई कुछ तरकीबों को एक साथ आज़माना है, तो बोनस पॉइंट पाने के लिए, थोड़ी जटिलता और दोहराव से परेशान न हों.
सबसे पहले, हम प्रीलोडर के लिए सब-रिसॉर्स का एलान जोड़ते हैं:
<link rel="subresource" href="//other-domain.com/1.js">
<link rel="subresource" href="2.js">
इसके बाद, हम दस्तावेज़ के हेड में इनलाइन, async=false
का इस्तेमाल करके JavaScript के साथ अपनी स्क्रिप्ट लोड करते हैं. इसके बाद, IE की readystate पर आधारित स्क्रिप्ट लोड करने की सुविधा का इस्तेमाल किया जाता है. इसके बाद, स्क्रिप्ट लोड होने में लगने वाले समय को बढ़ाया जाता है.
var scripts = [
'1.js',
'2.js'
];
var src;
var script;
var pendingScripts = [];
var firstScript = document.scripts[0];
// Watch scripts load in IE
function stateChange() {
// Execute as many scripts in order as we can
var pendingScript;
while (pendingScripts[0] && pendingScripts[0].readyState == 'loaded') {
pendingScript = pendingScripts.shift();
// avoid future loading events from this script (eg, if src changes)
pendingScript.onreadystatechange = null;
// can't just appendChild, old IE bug if element isn't closed
firstScript.parentNode.insertBefore(pendingScript, firstScript);
}
}
// loop through our script urls
while (src = scripts.shift()) {
if ('async' in firstScript) { // modern browsers
script = document.createElement('script');
script.async = false;
script.src = src;
document.head.appendChild(script);
}
else if (firstScript.readyState) { // IE<10
// create a script and add it to our todo pile
script = document.createElement('script');
pendingScripts.push(script);
// listen for state changes
script.onreadystatechange = stateChange;
// must set src AFTER adding onreadystatechange listener
// else we'll miss the loaded event for cached scripts
script.src = src;
}
else { // fall back to defer
document.write('<script src="' + src + '" defer></'+'script>');
}
}
कुछ ट्रिक और छोटा करने के बाद, यह 362 बाइट + आपकी स्क्रिप्ट के यूआरएल हो जाता है:
!function(e,t,r){function n(){for(;d[0]&&"loaded"==d[0][f];)c=d.shift(),c[o]=!i.parentNode.insertBefore(c,i)}for(var s,a,c,d=[],i=e.scripts[0],o="onreadystatechange",f="readyState";s=r.shift();)a=e.createElement(t),"async"in i?(a.async=!1,e.head.appendChild(a)):i[f]?(d.push(a),a[o]=n):e.write("<"+t+' src="'+s+'" defer></'+t+">"),a.src=s}(document,"script",[
"//other-domain.com/1.js",
"2.js"
])
क्या यह किसी सामान्य स्क्रिप्ट की तुलना में ज़्यादा बाइट के लायक है? अगर स्क्रिप्ट को शर्तों के साथ लोड करने के लिए, JavaScript का इस्तेमाल पहले से किया जा रहा है, जैसा कि बीबीसी करता है, तो आपको उन डाउनलोड को पहले से ट्रिगर करने से भी फ़ायदा हो सकता है. अगर ऐसा नहीं है, तो हो सकता है कि आपके लिए यह तरीका काम न करे. ऐसे में, बॉडी के आखिर में एट्रिब्यूट जोड़ने का आसान तरीका अपनाएं.
अब मुझे पता है कि WHATWG स्क्रिप्ट लोड करने का सेक्शन इतना बड़ा क्यों है. मुझे एक ड्रिंक चाहिए.
एक नज़र में जानकारी
प्लेन स्क्रिप्ट एलिमेंट
<script src="//other-domain.com/1.js"></script>
<script src="2.js"></script>
स्पेसिफ़िकेशन के मुताबिक: सभी को एक साथ डाउनलोड करें. किसी भी सीएसएस के इंतज़ार में होने पर, उसे क्रम से एक्ज़ीक्यूट करें. साथ ही, पूरा होने तक रेंडरिंग को ब्लॉक करें. ब्राउज़र का जवाब: हां, सर!
टालें
<script src="//other-domain.com/1.js" defer></script>
<script src="2.js" defer></script>
खास जानकारी में कहा गया है: साथ में डाउनलोड करें और DOMContentLoaded से ठीक पहले क्रम से चलाएं. “src” के बिना स्क्रिप्ट पर “defer” को अनदेखा करें. IE < 10 का कहना है: हो सकता है कि मैं 1.js के आधे हिस्से को लागू करने के बाद, 2.js को लागू करूं. है न? लाल रंग के ब्राउज़र का कहना है: मुझे नहीं पता कि यह “डिफ़र” क्या है. मैं स्क्रिप्ट इस तरह लोड कर रहा हूं, जैसे कि वे स्क्रिप्ट ही नहीं कर रही हों. अन्य ब्राउज़र कहते हैं: ठीक है, लेकिन हो सकता है कि मैं “src” के बिना स्क्रिप्ट पर “defer” को अनदेखा न करूं.
Async
<script src="//other-domain.com/1.js" async></script>
<script src="2.js" async></script>
स्पेसिफ़िकेशन के मुताबिक: एक साथ डाउनलोड करें और जिस क्रम में डाउनलोड किए गए हैं उसी क्रम में चलाएं. लाल रंग में दिख रहे ब्राउज़र के मुताबिक: ‘async’ क्या है? मैं स्क्रिप्ट को ऐसे लोड करूंगा जैसे कि यह मौजूद ही नहीं है. अन्य ब्राउज़र के मुताबिक: हां, ठीक है.
Async false
[
'1.js',
'2.js'
].forEach(function(src) {
var script = document.createElement('script');
script.src = src;
script.async = false;
document.head.appendChild(script);
});
खास जानकारी में कहा गया है: एक साथ डाउनलोड करें और पूरा डाउनलोड होते ही उसे क्रम में लगाएं. Firefox < 3.6, Opera का कहना है: मुझे नहीं पता कि यह “async” क्या है, लेकिन ऐसा होता है कि JS के ज़रिए जोड़ी गई स्क्रिप्ट, जोड़े जाने के क्रम में ही चलती हैं. Safari 5.0 का कहना है: मुझे “async” के बारे में पता है, लेकिन JS की मदद से इसे “false” पर सेट करने के बारे में नहीं पता. आपकी स्क्रिप्ट मिलने के बाद, हम उन्हें किसी भी क्रम में लागू कर देंगे. IE < 10 का कहना है: “एक साथ काम नहीं करने वाली स्क्रिप्ट” के बारे में कोई आइडिया नहीं है, लेकिन “onreadystatechange” का इस्तेमाल करने का एक तरीका है. अन्य लाल ब्राउज़र का कहना है: मुझे यह “एक साथ काम नहीं करने वाली” चीज़ समझ नहीं आ रही, मैं आपकी स्क्रिप्ट के उतरते ही उन्हें लागू कर दूंगा, वह भी किसी भी क्रम में. अन्य सभी चीज़ें बताती हैं: मैं आपका दोस्त हूं, हम इस काम को नियमों के मुताबिक करेंगे.