प्रकाशित: 31 मार्च, 2014
रेंडरिंग पाथ की परफ़ॉर्मेंस में आने वाली समस्याओं की पहचान करने और उन्हें ठीक करने के लिए, आम तौर पर होने वाली गड़बड़ियों के बारे में अच्छी जानकारी होना ज़रूरी है. सामान्य प्रदर्शन पैटर्न की पहचान करने वाले निर्देशित टूर की सहायता से आप अपने पेज अनुकूलित कर सकते हैं.
ज़रूरी रेंडरिंग पाथ को ऑप्टिमाइज़ करने से, ब्राउज़र जल्द से जल्द पेज को पेंट कर पाता है: तेज़ी से लोड होने वाले पेजों से ज़्यादा यूज़र ऐक्टिविटी, ज़्यादा पेज, और बेहतर कन्वर्ज़न. वेबसाइट पर आने वाला व्यक्ति, खाली स्क्रीन को देखते समय कम से कम समय बिता सकता है. इसके लिए, हमें लोड होने वाले रिसॉर्स और उनके क्रम को ऑप्टिमाइज़ करना होगा.
इस प्रोसेस के बारे में समझाने के लिए, सबसे आसान केस से शुरुआत करें. साथ ही, हमारा पेज लगातार तैयार करें, ताकि उसमें अतिरिक्त संसाधन, स्टाइल, और ऐप्लिकेशन के लॉजिक को शामिल किया जा सके. इस प्रोसेस में, हम हर मामले को ऑप्टिमाइज़ करेंगे. साथ ही, यह भी देखेंगे कि कहां गड़बड़ी हो सकती है.
अब तक हमने सिर्फ़ इस बात पर फ़ोकस किया है कि रिसॉर्स (सीएसएस, JS या एचटीएमएल फ़ाइल) प्रोसेस होने के बाद, ब्राउज़र में क्या होता है. हमने कैश मेमोरी या नेटवर्क से संसाधन को फ़ेच करने में लगने वाले समय को अनदेखा किया है. हम इन बातों को मानकर चलेंगे:
- सर्वर तक नेटवर्क में दोतरफ़ा यात्रा (प्रोपगेशन लेटेंसी) में 100 मिलीसेकंड लगते हैं.
- एचटीएमएल दस्तावेज़ के लिए सर्वर से जवाब मिलने का समय 100 मि॰से॰ और बाकी सभी फ़ाइलों के लिए 10 मि॰से॰ है.
Hello world का अनुभव
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Critical Path: No Style</title>
</head>
<body>
<p>Hello <span>web performance</span> students!</p>
<div><img src="awesome-photo.jpg" /></div>
</body>
</html>
बेसिक एचटीएमएल मार्कअप और सिंगल इमेज से शुरू करें; कोई CSS या JavaScript नहीं होना चाहिए. इसके बाद, Chrome DevTools में नेटवर्क पैनल खोलें और उससे मिलने वाले रिसॉर्स वॉटरफ़ॉल की जांच करें:
उम्मीद के मुताबिक, एचटीएमएल फ़ाइल को डाउनलोड होने में करीब 200 मि॰से॰ का समय लगा. ध्यान दें कि नीली लाइन के पारदर्शी हिस्से से पता चलता है कि ब्राउज़र ने बिना कोई रिस्पॉन्स बाइट पाए नेटवर्क पर कितनी देर इंतज़ार किया. वहीं, सॉलिड हिस्से से पता चलता है कि पहला रिस्पॉन्स बाइट मिलने के बाद, डाउनलोड पूरा होने में कितना समय लगा. एचटीएमएल डाउनलोड बहुत छोटा (<4K) होता है. इसलिए, पूरी फ़ाइल को फ़ेच करने के लिए, हमें सिर्फ़ एक राउंड ट्रिप की ज़रूरत होती है. इस वजह से, HTML दस्तावेज़ को फ़ेच करने में करीब 200 मिलीसेकंड लगते हैं. इसमें से आधा समय नेटवर्क के इंतज़ार में और आधा समय सर्वर के रिस्पॉन्स के इंतज़ार में बीतता है.
जब एचटीएमएल कॉन्टेंट उपलब्ध हो जाता है, तो ब्राउज़र बाइट को पार्स करके उन्हें टोकन में बदल देता है. साथ ही, डीओएम ट्री बनाता है. ध्यान दें कि DevTools, DOMContentLoaded इवेंट के लिए नीचे (216 मिलीसेकंड) आसानी से समय की रिपोर्ट करता है. यह नीली वर्टिकल लाइन से भी मेल खाता है. एचटीएमएल डाउनलोड होने के आखिर और नीली वर्टिकल लाइन (DOMContentLoaded) के बीच का अंतर, ब्राउज़र को DOM ट्री बनाने में लगने वाला समय होता है. इस मामले में, यह अंतर सिर्फ़ कुछ मिलीसेकंड का है.
ध्यान दें कि हमारी "बहुत बढ़िया फ़ोटो" domContentLoaded
इवेंट को ब्लॉक नहीं किया गया. ऐसा करने पर, हम रेंडर ट्री बना सकते हैं और पेज पर हर ऐसेट का इंतज़ार किए बिना पेज को पेंट भी कर सकते हैं: तेज़ पहला पेंट डिलीवर करने के लिए सभी संसाधन ज़रूरी नहीं हैं. दरअसल, जब हम ज़रूरी रेंडरिंग पाथ की बात करते हैं, तो हम आम तौर पर एचटीएमएल मार्कअप, सीएसएस, और JavaScript के बारे में बात करते हैं. इमेज, पेज के शुरुआती रेंडर होने से नहीं रोकतीं. हालांकि, हमें इमेज को जल्द से जल्द पेंट करने की कोशिश करनी चाहिए.
हालांकि, इमेज पर load
इवेंट (जिसे onload
भी कहा जाता है) ब्लॉक किया गया है: DevTools, onload
इवेंट को 335 मिलीसेकंड पर रिपोर्ट करता है. याद रखें कि onload
इवेंट उस पॉइंट को मार्क करता है जहां पेज के लिए ज़रूरी सभी रिसॉर्स डाउनलोड और प्रोसेस हो चुके होते हैं. इस पॉइंट पर, ब्राउज़र में लोडिंग स्पिनर (वॉटरफ़ॉल में लाल वर्टिकल लाइन) घूमना बंद हो सकता है.
मिक्स में JavaScript और CSS जोड़ना
हमारा "Hello World experience" पेज सामान्य दिखता है, लेकिन इसमें बहुत कुछ होता है. असल में हमें सिर्फ़ एचटीएमएल के अलावा दूसरी चीज़ों की ज़रूरत होती है: संभावना है कि हमारे पास एक सीएसएस स्टाइल शीट और एक या उससे ज़्यादा स्क्रिप्ट होंगी, ताकि हमारे पेज पर कुछ इंटरैक्टिविटी जोड़ी जा सके. दोनों को मिक्स में जोड़ें, ताकि यह पता चल सके कि क्या होता है:
<!DOCTYPE html>
<html>
<head>
<title>Critical Path: Measure Script</title>
<meta name="viewport" content="width=device-width,initial-scale=1" />
<link href="style.css" rel="stylesheet" />
</head>
<body onload="measureCRP()">
<p>Hello <span>web performance</span> students!</p>
<div><img src="awesome-photo.jpg" /></div>
<script src="timing.js"></script>
</body>
</html>
JavaScript और सीएसएस जोड़ने से पहले:
JavaScript और सीएसएस के साथ:
बाहरी सीएसएस और JavaScript फ़ाइलें जोड़ने से, हमारे वॉटरफ़ॉल में दो अतिरिक्त अनुरोध जुड़ जाते हैं. ब्राउज़र, इन सभी अनुरोधों को लगभग एक ही समय पर डिस्पैच करता है. हालांकि, ध्यान दें कि अब domContentLoaded
और onload
इवेंट के बीच समय का अंतर बहुत कम है.
क्या हुआ?
- हमारे सादे एचटीएमएल उदाहरण के विपरीत, हमें सीएसएसओएम बनाने के लिए सीएसएस फ़ाइल को फ़ेच और पार्स भी करना होगा. साथ ही, रेंडर ट्री बनाने के लिए, हमें डीओएम और सीएसएसओएम, दोनों की ज़रूरत होगी.
- पेज में पार्स करने से रोकने वाली JavaScript फ़ाइल भी शामिल है. इसलिए,
domContentLoaded
इवेंट तब तक ब्लॉक रहता है, जब तक सीएसएस फ़ाइल डाउनलोड और पार्स नहीं हो जाती: JavaScript, CSSOM से क्वेरी कर सकता है. इसलिए, JavaScript को लागू करने से पहले, हमें सीएसएस फ़ाइल को तब तक ब्लॉक करना होगा, जब तक वह डाउनलोड नहीं हो जाती.
अगर हम अपनी बाहरी स्क्रिप्ट को इनलाइन स्क्रिप्ट से बदल दें, तो क्या होगा? भले ही स्क्रिप्ट को सीधे पेज में इनलाइन किया गया हो, लेकिन ब्राउज़र तब तक इसे लागू नहीं कर सकता, जब तक सीएसएसओएम नहीं बन जाता. संक्षेप में, इनलाइन JavaScript भी पार्सर ब्लॉकिंग है.
हालांकि, सीएसएस को ब्लॉक करने के बावजूद, क्या स्क्रिप्ट को इनलाइन करने से पेज तेज़ी से रेंडर होता है? इसे आज़माकर देखें कि क्या होता है.
बाहरी JavaScript:
इनलाइन किया गया JavaScript:
हम एक कम अनुरोध कर रहे हैं, लेकिन हमारे onload
और domContentLoaded
बार, दोनों असल में एक जैसे हैं. क्यों? हम जानते हैं कि इससे कोई फ़र्क़ नहीं पड़ता कि JavaScript को इनलाइन किया गया है या बाहरी, क्योंकि जैसे ही ब्राउज़र स्क्रिप्ट टैग पर हिट करता है, वह ब्लॉक हो जाता है और CSSOM बनने तक इंतज़ार करता है. इसके अलावा, हमारे पहले उदाहरण में, ब्राउज़र सीएसएस और JavaScript, दोनों को एक साथ डाउनलोड करता है और वे लगभग एक ही समय में डाउनलोड हो जाते हैं. इस मामले में, JavaScript कोड को इनलाइन करने से हमें ज़्यादा सहायता नहीं मिलती है. हालांकि, ऐसी कई रणनीतियां हैं जिनसे हमारा पेज तेज़ी से रेंडर हो सकता है.
सबसे पहले, याद रखें कि सभी इनलाइन स्क्रिप्ट पार्सर को ब्लॉक करती हैं. हालांकि, बाहरी स्क्रिप्ट के लिए हम पार्सर को अनब्लॉक करने के लिए async
एट्रिब्यूट जोड़ सकते हैं. इनलाइन करने की सुविधा को पहले जैसा करें और फिर से कोशिश करें:
<!DOCTYPE html>
<html>
<head>
<title>Critical Path: Measure Async</title>
<meta name="viewport" content="width=device-width,initial-scale=1" />
<link href="style.css" rel="stylesheet" />
</head>
<body onload="measureCRP()">
<p>Hello <span>web performance</span> students!</p>
<div><img src="awesome-photo.jpg" /></div>
<script async src="timing.js"></script>
</body>
</html>
पार्सर-ब्लॉकिंग (बाहरी) JavaScript:
असाइन (बाहरी) JavaScript:
बहुत बेहतर! एचटीएमएल को पार्स करने के कुछ समय बाद, domContentLoaded
इवेंट फ़ायर हो जाता है; ब्राउज़र जानता है कि JavaScript पर ब्लॉक नहीं करना है और कोई दूसरी पार्सर ब्लॉक करने वाली स्क्रिप्ट नहीं है, इसलिए CSSOM बनाना भी साथ-साथ आगे बढ़ सकता है.
इसके अलावा, हम सीएसएस और JavaScript, दोनों को इनलाइन कर सकते थे:
<!DOCTYPE html>
<html>
<head>
<title>Critical Path: Measure Inlined</title>
<meta name="viewport" content="width=device-width,initial-scale=1" />
<style>
p {
font-weight: bold;
}
span {
color: red;
}
p span {
display: none;
}
img {
float: right;
}
</style>
</head>
<body>
<p>Hello <span>web performance</span> students!</p>
<div><img src="awesome-photo.jpg" /></div>
<script>
var span = document.getElementsByTagName('span')[0];
span.textContent = 'interactive'; // change DOM text content
span.style.display = 'inline'; // change CSSOM property
// create a new element, style it, and append it to the DOM
var loadTime = document.createElement('div');
loadTime.textContent = 'You loaded this page on: ' + new Date();
loadTime.style.color = 'blue';
document.body.appendChild(loadTime);
</script>
</body>
</html>
ध्यान दें कि domContentLoaded
का समय, पिछले उदाहरण में बताए गए समय के बराबर है. हमने अपने JavaScript को असाइन के तौर पर मार्क करने के बजाय, सीएसएस और JS, दोनों को पेज में इनलाइन किया है. इससे हमारा एचटीएमएल पेज काफ़ी बड़ा हो जाता है, लेकिन इसका फ़ायदा यह है कि ब्राउज़र को किसी बाहरी रिसॉर्स को फ़ेच करने के लिए इंतज़ार नहीं करना पड़ता; सब कुछ पेज पर मौजूद है.
जैसा कि आपको दिख रहा है कि बेहद बुनियादी पेज होने के बावजूद, क्रिटिकल रेंडरिंग पाथ को ऑप्टिमाइज़ करना आसान नहीं है. हमें अलग-अलग रिसॉर्स के डिपेंडेंसी ग्राफ़ को समझना होगा. साथ ही, हमें यह पता लगाना होगा कि कौन से रिसॉर्स "अहम" हैं, और पेज पर उन संसाधनों को शामिल करने के लिए हमें अलग-अलग रणनीतियों में से कोई एक चुनना होगा. इस समस्या का कोई एक समाधान नहीं है; तो हर पेज अलग है. सबसे सही रणनीति तय करने के लिए, आपको खुद भी इसी तरह की प्रोसेस अपनानी होगी.
इसके बावजूद, देखें कि क्या हम वापस आकर कुछ सामान्य प्रदर्शन पैटर्न की पहचान कर सकते हैं.
परफ़ॉर्मेंस के पैटर्न
सबसे आसान पेज में सिर्फ़ एचटीएमएल मार्कअप होता है; कोई सीएसएस, JavaScript या दूसरी तरह के संसाधन नहीं. इस पेज को रेंडर करने के लिए, ब्राउज़र को अनुरोध शुरू करना होता है, एचटीएमएल दस्तावेज़ के आने का इंतज़ार करना होता है, उसे पार्स करना होता है, डीओएम बनाना होता है, और आखिर में उसे स्क्रीन पर रेंडर करना होता है:
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Critical Path: No Style</title>
</head>
<body>
<p>Hello <span>web performance</span> students!</p>
<div><img src="awesome-photo.jpg" /></div>
</body>
</html>
T0 और T1 के बीच का समय, नेटवर्क और सर्वर को प्रोसेस करने में लगने वाला समय दिखाता है. अगर एचटीएमएल फ़ाइल छोटी है, तो सबसे अच्छे मामले में, सिर्फ़ एक नेटवर्क राउंड ट्रिप से पूरा दस्तावेज़ फ़ेच हो जाता है. टीसीपी ट्रांसपोर्ट प्रोटोकॉल के काम करने के तरीके की वजह से, बड़ी फ़ाइलों के लिए ज़्यादा राउंड ट्रिप की ज़रूरत पड़ सकती है. इस वजह से, सबसे अच्छे मामले में ऊपर दिए गए पेज में, एक राउंड ट्रिप (कम से कम) क्रिटिकल रेंडरिंग पाथ होता है.
अब उसी पेज पर विचार करें, लेकिन बाहरी सीएसएस फ़ाइल के साथ:
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width,initial-scale=1" />
<link href="style.css" rel="stylesheet" />
</head>
<body>
<p>Hello <span>web performance</span> students!</p>
<div><img src="awesome-photo.jpg" /></div>
</body>
</html>
एक बार फिर से, हमें एचटीएमएल दस्तावेज़ फ़ेच करने के लिए एक नेटवर्क राउंडट्रिप शुरू करनी होती है. इसके बाद, मिले मार्कअप से हमें पता चलता है कि हमें सीएसएस फ़ाइल की भी ज़रूरत है; इसका मतलब है कि स्क्रीन पर पेज को रेंडर करने से पहले, ब्राउज़र को सर्वर पर वापस जाना होगा और सीएसएस की मदद लेनी होगी. इस वजह से, इस पेज को दिखाने से पहले कम से कम दो राउंडट्रिप होती हैं. फिर से, सीएसएस फ़ाइल को कई राउंड ट्रिप लग सकते हैं. इसलिए, "कम से कम" पर ज़ोर दिया गया है.
क्रिटिकल रेंडरिंग पाथ के बारे में बताने के लिए, हम इन शब्दों का इस्तेमाल करते हैं:
- ज़रूरी रिसॉर्स: ऐसा रिसॉर्स जो पेज की शुरुआती रेंडरिंग को ब्लॉक कर सकता है.
- क्रिटिकल पाथ की लंबाई: राउंड ट्रिप की संख्या या सभी ज़रूरी संसाधनों को फ़ेच करने में लगने वाला कुल समय.
- ज़रूरी बाइट: पेज को पहली बार रेंडर करने के लिए ज़रूरी बाइट की कुल संख्या. यह सभी ज़रूरी रिसॉर्स की ट्रांसफ़र फ़ाइल साइज़ का कुल योग होता है. हमारे पहले उदाहरण में, एक एचटीएमएल पेज के साथ एक ही क्रिटिकल रिसॉर्स (एचटीएमएल दस्तावेज़) था. क्रिटिकल पाथ की लंबाई भी एक नेटवर्क राउंड ट्रिप के बराबर थी (यह मानते हुए कि फ़ाइल छोटी थी) और कुल क्रिटिकल बाइट, सिर्फ़ एचटीएमएल दस्तावेज़ के ट्रांसफ़र साइज़ के बराबर थे.
अब इसकी तुलना, पिछले एचटीएमएल और सीएसएस उदाहरण के क्रिटिकल पाथ की विशेषताओं से करें:
- 2 ज़रूरी संसाधन
- क्रिटिकल पाथ की कम से कम लंबाई के लिए, 2 या उससे ज़्यादा राउंड ट्रिप
- 9 केबी के अहम बाइट
रेंडर ट्री बनाने के लिए, हमें एचटीएमएल और सीएसएस, दोनों की ज़रूरत होती है. इसकी वजह से, एचटीएमएल और सीएसएस दोनों ही अहम रिसॉर्स होते हैं: सीएसएस को सिर्फ़ तब फ़ेच किया जाता है, जब ब्राउज़र को एचटीएमएल दस्तावेज़ मिलता है. इसलिए, ज़रूरी पाथ की लंबाई कम से कम दो राउंडट्रिप होनी चाहिए. दोनों संसाधनों में कुल 9 केबी के क्रिटिकल बाइट हैं.
अब मिक्स में एक और JavaScript फ़ाइल जोड़ें.
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width,initial-scale=1" />
<link href="style.css" rel="stylesheet" />
</head>
<body>
<p>Hello <span>web performance</span> students!</p>
<div><img src="awesome-photo.jpg" /></div>
<script src="app.js"></script>
</body>
</html>
हमने app.js
को जोड़ा है, जो पेज पर बाहरी JavaScript एसेट है. साथ ही, यह पार्सर ब्लॉकिंग (ज़रूरी) रिसॉर्स, दोनों है. इससे भी बुरा यह है कि JavaScript फ़ाइल को चलाने के लिए, हमें CSSOM को ब्लॉक करना होगा और तब तक इंतज़ार करना होगा, जब तक वह डाउनलोड नहीं हो जाता. याद रखें कि JavaScript, CSSOM से क्वेरी कर सकता है. इसलिए, style.css
डाउनलोड होने और CSSOM बनने तक ब्राउज़र रुक जाता है.
इसके बावजूद, अगर हम इस पेज के "नेटवर्क वॉटरफ़ॉल" पर नज़र डालें, तो आपको दिखेगा कि सीएसएस और JavaScript, दोनों के अनुरोध करीब-करीब एक ही समय पर किए जाते हैं; ब्राउज़र को एचटीएमएल मिलता है, दोनों रिसॉर्स को खोजा जाता है, और दोनों अनुरोध शुरू किए जाते हैं. इस वजह से, पिछली इमेज में दिखाए गए पेज के पाथ की ये अहम विशेषताएं हैं:
- 3 ज़रूरी संसाधन
- पाथ की कम से कम क्रिटिकल लंबाई के लिए 2 या उससे ज़्यादा राउंडट्रिप
- 11 केबी के अहम बाइट
अब हमारे पास तीन ज़रूरी संसाधन हैं, जिनमें कुल 11 केबी के ज़रूरी बाइट हैं. हालांकि, हमारे क्रिटिकल पाथ की लंबाई अब भी दो राउंड ट्रिप है, क्योंकि हम सीएसएस और JavaScript को एक साथ ट्रांसफ़र कर सकते हैं. अहम रेंडरिंग पाथ की विशेषताओं को समझने का मतलब है, अहम संसाधनों को पहचानना. साथ ही, यह भी समझना कि ब्राउज़र, फ़ीड पाने को कैसे शेड्यूल करेगा.
अपने साइट डेवलपर से चैट करने के बाद, हमें पता चला कि हमने अपने पेज पर जो JavaScript शामिल की है उसे ब्लॉक करने की ज़रूरत नहीं है; हमारे पास कुछ ऐसे एनालिटिक्स और अन्य कोड हैं, जिन्हें हमारे पेज की रेंडरिंग को ब्लॉक करने की ज़रूरत नहीं है. इस जानकारी के आधार पर, हम पार्सर को अनब्लॉक करने के लिए, <script>
एलिमेंट में async
एट्रिब्यूट जोड़ सकते हैं:
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width,initial-scale=1" />
<link href="style.css" rel="stylesheet" />
</head>
<body>
<p>Hello <span>web performance</span> students!</p>
<div><img src="awesome-photo.jpg" /></div>
<script src="app.js" async></script>
</body>
</html>
एसिंक्रोनस स्क्रिप्ट के कई फ़ायदे हैं:
- स्क्रिप्ट अब पार्स करने से नहीं रोकती और यह रेंडरिंग के अहम पाथ का हिस्सा नहीं है.
- कोई दूसरी ज़रूरी स्क्रिप्ट न होने की वजह से, सीएसएस को
domContentLoaded
इवेंट को ब्लॉक करने की ज़रूरत नहीं है. domContentLoaded
इवेंट जितनी जल्दी ट्रिगर होगा, ऐप्लिकेशन लॉजिक उतनी जल्दी एक्ज़ीक्यूट होगा.
इस वजह से, हमारा ऑप्टिमाइज़ किया गया पेज अब दो ज़रूरी संसाधनों (एचटीएमएल और सीएसएस) पर वापस आ गया है. साथ ही, इस पेज के क्रिटिकल पाथ की लंबाई कम से कम दो राउंड ट्रिप है और इसमें कुल 9 केबी के क्रिटिकल बाइट हैं.
आखिर में, अगर सीएसएस स्टाइल शीट सिर्फ़ प्रिंट के लिए ज़रूरी थी, तो वह कैसी दिखेगी?
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width,initial-scale=1" />
<link href="style.css" rel="stylesheet" media="print" />
</head>
<body>
<p>Hello <span>web performance</span> students!</p>
<div><img src="awesome-photo.jpg" /></div>
<script src="app.js" async></script>
</body>
</html>
style.css रिसॉर्स का इस्तेमाल सिर्फ़ प्रिंट के लिए किया जाता है. इसलिए, पेज को रेंडर करने के लिए ब्राउज़र को उस पर ब्लॉक करने की ज़रूरत नहीं होती. इसलिए, DOM का निर्माण पूरा होने के बाद, ब्राउज़र के पास पेज को रेंडर करने के लिए ज़रूरी जानकारी होती है. इस वजह से, इस पेज में सिर्फ़ एक ज़रूरी संसाधन (एचटीएमएल दस्तावेज़) है. रेंडरिंग के लिए कम से कम पाथ, एक राउंडट्रिप है.