अपने HTML5 ऐप्लिकेशन की परफ़ॉर्मेंस को बेहतर बनाना

परिचय

HTML5 की मदद से, वेब ऐप्लिकेशन के विज़ुअल को बेहतर बनाया जा सकता है. यह बात खास तौर पर ऐनिमेशन के लिए सही है. हालांकि, इस नई सुविधा के साथ नई चुनौतियां भी आती हैं. असल में, ये चुनौतियां ज़्यादा नई नहीं हैं. इसलिए, कभी-कभी अपने डेस्क के बगल में बैठी फ़्लैश प्रोग्रामर से पूछें कि उसने पहले ऐसी समस्याओं को कैसे हल किया था.

वैसे भी, एनिमेशन में काम करते समय यह बहुत ज़रूरी हो जाता है कि उपयोगकर्ताओं को ये एनिमेशन आसानी से दिखें. हमें यह समझना होगा कि ऐनिमेशन को आसानी से बनाने के लिए, हर सेकंड में फ़्रेम की संख्या को बढ़ाना ही काफ़ी नहीं है. माफ़ करें, हमारा दिमाग इससे ज़्यादा बेहतर काम करता है. आपको पता चलेगा कि हर सेकंड 30 फ़्रेम (एफ़पीएस) का ऐनिमेशन, बीच में कुछ फ़्रेम छोड़कर 60 एफ़पीएस के ऐनिमेशन से काफ़ी बेहतर होता है. लोगों को वीडियो में काटे-छांटे हिस्से पसंद नहीं आते.

इस लेख में, आपको अपने ऐप्लिकेशन के अनुभव को बेहतर बनाने के लिए टूल और तकनीकें दी गई हैं.

रणनीति

हम आपको HTML5 की मदद से शानदार और बेहतरीन विज़ुअल वाले ऐप्लिकेशन बनाने से रोकना नहीं चाहते.

जब आपको लगे कि परफ़ॉर्मेंस को और बेहतर किया जा सकता है, तो यहां वापस आकर पढ़ें कि अपने ऐप्लिकेशन के एलिमेंट को कैसे बेहतर बनाया जा सकता है. इससे, कुछ कामों को सही तरीके से करने में मदद मिल सकती है. हालांकि, कभी भी इस बात का ध्यान न रखें कि आपका काम सही तरीके से हो रहा है या नहीं.

HTML5 के साथ विज़ुअल फ़िडेलिटी++

हार्डवेयर से तेज़ी लाने की सुविधा

ब्राउज़र में रेंडरिंग की पूरी परफ़ॉर्मेंस के लिए, हार्डवेयर ऐक्सेलरेशन एक अहम माइलस्टोन है. आम तौर पर, उन टास्क को ऑफ़लोड किया जाता है जिन्हें मुख्य सीपीयू कैलकुलेट करता है. ये टास्क, आपके कंप्यूटर के ग्राफ़िक्स अडैप्टर में मौजूद ग्राफ़िक्स प्रोसेसिंग यूनिट (जीपीयू) को भेजे जाते हैं. इससे परफ़ॉर्मेंस में काफ़ी बढ़ोतरी हो सकती है. साथ ही, मोबाइल डिवाइसों पर संसाधनों के इस्तेमाल को भी कम किया जा सकता है.

आपके दस्तावेज़ के इन पहलुओं को GPU की मदद से तेज़ किया जा सकता है

  • सामान्य लेआउट कंपोज करना
  • CSS3 ट्रांज़िशन
  • CSS3 3D ट्रांसफ़ॉर्म
  • कैनवस पर ड्रॉइंग
  • WebGL 3D ड्रॉइंग

कैनवस और WebGL की परफ़ॉर्मेंस को बेहतर बनाने की सुविधाएं, खास मकसद के लिए बनाई गई हैं. हो सकता है कि ये सुविधाएं आपके ऐप्लिकेशन पर लागू न हों. हालांकि, पहले तीन पहलुओं से, ज़्यादातर ऐप्लिकेशन की परफ़ॉर्मेंस को बेहतर बनाने में मदद मिल सकती है.

किस प्रोसेस को तेज़ किया जा सकता है?

जीपीयू ऐक्सेलरेशन की सुविधा, खास काम के लिए बने हार्डवेयर पर, पहले से तय और खास कामों को ऑफ़लोड करके काम करती है. आम तौर पर, आपके दस्तावेज़ को कई "लेयर" में बांटा जाता है. ये लेयर, आपके पेज के उन हिस्सों से अलग होती हैं जिन्हें तेज़ी से लोड किया जाता है. इन लेयर को रेंडर करने के लिए, रेंडर करने की पारंपरिक पाइपलाइन का इस्तेमाल किया जाता है. इसके बाद, जीपीयू का इस्तेमाल करके लेयर को एक पेज पर कॉम्पोज़ किया जाता है. साथ ही, "इफ़ेक्ट" लागू किए जाते हैं, जिन्हें फ़्लाई पर तेज़ किया जा सकता है. ऐसा हो सकता है कि स्क्रीन पर ऐनिमेशन वाले किसी ऑब्जेक्ट के लिए, ऐनिमेशन के दौरान पेज को एक बार भी "फिर से व्यवस्थित" करने की ज़रूरत न पड़े.

आपको इस बात का ध्यान रखना होगा कि रेंडरिंग इंजन को यह पता लगाने में आसानी हो कि उसे जीपीयू ऐक्सेलरेशन का इस्तेमाल कब करना है. नीचे दिया गया उदाहरण देखें:

हालांकि, ब्राउज़र को यह पता नहीं चलता कि आपने ऐसा कुछ किया है जिसे कोई व्यक्ति आसानी से ऐनिमेशन के तौर पर देख सकता है. इसके बजाय, सीएसएस3 ट्रांज़िशन का इस्तेमाल करके, एक ही विज़ुअल दिखाने पर क्या होता है, इस पर ध्यान दें:

ब्राउज़र इस ऐनिमेशन को कैसे लागू करता है, यह डेवलपर के लिए पूरी तरह से छिपा होता है. इसका मतलब है कि ब्राउज़र, तय किए गए लक्ष्य को हासिल करने के लिए, जीपीयू ऐक्सेलरेशन जैसी तरकीबें अपना सकता है.

जीपीयू की परफ़ॉर्मेंस को बेहतर बनाने की सुविधा को डीबग करने में मदद करने के लिए, Chrome में दो काम के कमांड-लाइन फ़्लैग मौजूद हैं:

  1. --show-composited-layer-borders, उन एलिमेंट के चारों ओर लाल बॉर्डर दिखाता है जिनमें GPU लेवल पर बदलाव किया जा रहा है. यह पुष्टि करने के लिए अच्छा है कि आपके डेटा में बदलाव, GPU लेयर में हो रहे हैं.
  2. --show-paint-rects जीपीयू के अलावा, अन्य सभी बदलावों को पेंट किया जाता है. इससे, फिर से पेंट किए गए सभी हिस्सों के चारों ओर हल्का बॉर्डर दिखता है. ब्राउज़र, पेंट किए जाने वाले हिस्सों को ऑप्टिमाइज़ करता हुआ दिख रहा है.

Safari में भी मिलते-जुलते रनटाइम फ़्लैग होते हैं, जिनके बारे में यहां बताया गया है.

CSS3 ट्रांज़िशन

सीएसएस ट्रांज़िशन की मदद से, स्टाइल ऐनिमेशन को आसानी से लागू किया जा सकता है. साथ ही, यह परफ़ॉर्मेंस को बेहतर बनाने वाली एक स्मार्ट सुविधा भी है. सीएसएस ट्रांज़िशन को ब्राउज़र मैनेज करता है. इसलिए, इसके ऐनिमेशन की फ़िडेलिटी को काफ़ी बेहतर बनाया जा सकता है. साथ ही, कई मामलों में हार्डवेयर की मदद से इसे तेज़ किया जा सकता है. फ़िलहाल, WebKit (Chrome, Safari, iOS) में हार्डवेयर से तेज़ी लाने की सुविधा के साथ सीएसएस ट्रांसफ़ॉर्मेशन की सुविधा है. हालांकि, यह सुविधा जल्द ही अन्य ब्राउज़र और प्लैटफ़ॉर्म पर भी उपलब्ध होगी.

transitionEnd इवेंट का इस्तेमाल करके, इस स्क्रिप्ट को बेहतर कॉम्बिनेशन में बदला जा सकता है. हालांकि, फ़िलहाल ट्रांज़िशन के खत्म होने के सभी इवेंट कैप्चर करने का मतलब है कि webkitTransitionEnd transitionend oTransitionEnd देखा जा रहा है.

कई लाइब्रेरी में अब ऐनिमेशन एपीआई जोड़े गए हैं. ये एपीआई, ट्रांज़िशन मौजूद होने पर उसका फ़ायदा लेते हैं और मौजूद न होने पर स्टैंडर्ड डीओएम स्टाइल ऐनिमेशन का इस्तेमाल करते हैं. जैसे, scripty2, YUI ट्रांज़िशन, jQuery ऐनिमेट बेहतर.

CSS3 Translate

हमें यकीन है कि आपने पहले भी पेज पर किसी एलिमेंट की x/y पोज़िशन को ऐनिमेट किया होगा. ऐसा हो सकता है कि आपने इनलाइन स्टाइल की लेफ़्ट और टॉप प्रॉपर्टी में बदलाव किया हो. 2D ट्रांसफ़ॉर्म की मदद से, इस व्यवहार को दोहराने के लिए translate() फ़ंक्शन का इस्तेमाल किया जा सकता है.

हम इसे DOM ऐनिमेशन के साथ कॉम्बो कर सकते हैं, ताकि सबसे बेहतर तरीके का इस्तेमाल किया जा सके

<div style="position:relative; height:120px;" class="hwaccel">

  <div style="padding:5px; width:100px; height:100px; background:papayaWhip;
              position:absolute;" id="box">
  </div>
</div>

<script>
document.querySelector('#box').addEventListener('click', moveIt, false);

function moveIt(evt) {
  var elem = evt.target;

  if (Modernizr.csstransforms && Modernizr.csstransitions) {
    // vendor prefixes omitted here for brevity
    elem.style.transition = 'all 3s ease-out';
    elem.style.transform = 'translateX(600px)';

  } else {
    // if an older browser, fall back to jQuery animate
    jQuery(elem).animate({ 'left': '600px'}, 3000);
  }
}
</script>

हम Modernizr का इस्तेमाल, सीएसएस 2D ट्रांसफ़ॉर्म और सीएसएस ट्रांज़िशन की सुविधा की जांच करने के लिए करते हैं. अगर ऐसा है, तो हम पोज़िशन बदलने के लिए translate का इस्तेमाल करेंगे. अगर ट्रांज़िशन का इस्तेमाल करके इसे ऐनिमेट किया गया है, तो हो सकता है कि ब्राउज़र इसे हार्डवेयर की मदद से तेज़ कर सके. ब्राउज़र को सही दिशा में आगे बढ़ाने के लिए, हम ऊपर दिए गए "मैजिक सीएसएस बुलेट" का इस्तेमाल करेंगे.

अगर हमारा ब्राउज़र कम क्षमता वाला है, तो हम अपने एलिमेंट को मूव करने के लिए jQuery का इस्तेमाल करेंगे. इस पूरी प्रोसेस को अपने-आप पूरा करने के लिए, Louis-Remi Babe का jQuery Transform polyfill प्लग इन इस्तेमाल किया जा सकता है.

window.requestAnimationFrame

requestAnimationFrame को Mozilla ने लॉन्च किया था और WebKit ने इसे बेहतर बनाया था. इसका मकसद, ऐनिमेशन चलाने के लिए आपको एक नेटिव एपीआई उपलब्ध कराना था. भले ही, वे ऐनिमेशन DOM/CSS पर आधारित हों या <canvas> या WebGL पर. ब्राउज़र, एक साथ चल रहे ऐनिमेशन को एक ही रीफ़्लो और रीपेंट साइकल में ऑप्टिमाइज़ कर सकता है. इससे बेहतर क्वालिटी वाला ऐनिमेशन मिलता है. उदाहरण के लिए, सीएसएस ट्रांज़िशन या SVG SMIL के साथ सिंक किए गए JS-आधारित ऐनिमेशन. साथ ही, अगर ऐनिमेशन लूप को किसी ऐसे टैब में चलाया जा रहा है जो नहीं दिख रहा है, तो ब्राउज़र उसे चलाना बंद कर देगा. इसका मतलब है कि सीपीयू, जीपीयू, और मेमोरी का कम इस्तेमाल होगा. इससे बैटरी लाइफ़ ज़्यादा लंबी होगी.

requestAnimationFrame का इस्तेमाल कैसे और क्यों करना है, इस बारे में ज़्यादा जानने के लिए, पॉल आयरिश का लेख स्मार्ट ऐनिमेशन के लिए requestAnimationFrame देखें.

प्रोफ़ाइल बनाना

जब आपको पता चलता है कि आपके ऐप्लिकेशन की स्पीड को बेहतर किया जा सकता है, तो प्रोफ़ाइलिंग की मदद से यह पता लगाएं कि ऑप्टिमाइज़ेशन से सबसे ज़्यादा फ़ायदा कहां मिल सकता है. ऑप्टिमाइज़ेशन से, अक्सर आपके सोर्स कोड को मैनेज करने पर बुरा असर पड़ता है. इसलिए, इसे सिर्फ़ ज़रूरत पड़ने पर ही लागू करें. प्रोफ़ाइलिंग से आपको यह पता चलता है कि आपके कोड के किन हिस्सों की परफ़ॉर्मेंस को बेहतर बनाने से सबसे ज़्यादा फ़ायदा होगा.

JavaScript प्रोफ़ाइलिंग

JavaScript प्रोफ़ाइलर, JavaScript फ़ंक्शन लेवल पर आपके ऐप्लिकेशन की परफ़ॉर्मेंस की खास जानकारी देते हैं. इसके लिए, वे हर फ़ंक्शन को शुरू होने से लेकर खत्म होने तक लगने वाले समय को मेज़र करते हैं.

किसी फ़ंक्शन को ऊपर से नीचे तक पूरा करने में लगने वाला कुल समय, फ़ंक्शन के पूरे होने में लगने वाला समय कहलाता है. फ़ंक्शन को एक्ज़ीक्यूट करने में लगने वाला कुल समय, फ़ंक्शन को एक्ज़ीक्यूट करने में लगने वाले समय में से, फ़ंक्शन से कॉल किए गए फ़ंक्शन को एक्ज़ीक्यूट करने में लगने वाले समय को घटाने पर मिलता है.

कुछ फ़ंक्शन, दूसरे फ़ंक्शन की तुलना में ज़्यादा बार कॉल किए जाते हैं. आम तौर पर, प्रोफ़ाइलर आपको सभी इनवोकेशन के चलने में लगने वाला समय बताते हैं. साथ ही, वे आपको कम से कम, ज़्यादा से ज़्यादा, और औसत समय भी बताते हैं.

ज़्यादा जानकारी के लिए, Chrome DevTools में प्रोफ़ाइलिंग के बारे में दस्तावेज़ देखें.

डीओएम

JavaScript की परफ़ॉर्मेंस से यह तय होता है कि आपका ऐप्लिकेशन कितना आसानी से इस्तेमाल किया जा सकता है और कितना तेज़ी से काम करता है. यह समझना ज़रूरी है कि JavaScript प्रोफ़ाइलर, आपके JavaScript के लागू होने का समय मेज़र करते हैं. साथ ही, वे डीओएम ऑपरेशन में लगने वाले समय को भी अप्रत्यक्ष रूप से मेज़र करते हैं. आम तौर पर, परफ़ॉर्मेंस से जुड़ी समस्याओं की वजह ये डीओएम ऑपरेशन होते हैं.

function drawArray(array) {
  for(var i = 0; i < array.length; i++) {
    document.getElementById('test').innerHTML += array[i]; // No good :(
  }
}

उदाहरण के लिए, ऊपर दिए गए कोड में असल JavaScript को लागू करने में काफ़ी कम समय लगता है. हालांकि, इस बात की बहुत संभावना है कि drawArray-फ़ंक्शन आपकी प्रोफ़ाइलों में दिखेगा, क्योंकि यह DOM के साथ बहुत खराब तरीके से इंटरैक्ट कर रहा है.

सुझाव और तरकीबें

बिना नाम वाले फ़ंक्शन

बिना नाम वाले फ़ंक्शन की प्रोफ़ाइल आसानी से नहीं बनाई जा सकती, क्योंकि इनका कोई नाम नहीं होता. इसलिए, ये प्रोफ़ाइलर में नहीं दिखते. इस समस्या को हल करने के दो तरीके हैं:

$('.stuff').each(function() { ... });

इस तरह से फिर से लिखें:

$('.stuff').each(function workOnStuff() { ... });

आम तौर पर, यह नहीं पता होता कि JavaScript में फ़ंक्शन एक्सप्रेशन को नाम दिया जा सकता है. ऐसा करने पर, वे प्रोफ़ाइलर में सही तरीके से दिखेंगे. इस समाधान में एक समस्या है: नाम वाला एक्सप्रेशन, फ़ंक्शन के नाम को मौजूदा लेक्सिकल स्कोप में डालता है. इससे अन्य सिंबल मिट सकते हैं, इसलिए सावधानी बरतें.

लंबे फ़ंक्शन की प्रोफ़ाइल बनाना

मान लें कि आपके पास एक लंबा फ़ंक्शन है और आपको लगता है कि परफ़ॉर्मेंस से जुड़ी समस्याओं की वजह, इसका एक छोटा हिस्सा हो सकता है. यह पता लगाने के दो तरीके हैं कि समस्या किस हिस्से में है:

  1. सही तरीका: अपने कोड को फिर से लिखें, ताकि उसमें कोई लंबा फ़ंक्शन शामिल न हो.
  2. काम को पूरा करने का गलत तरीका: अपने कोड में, नाम वाले ऐसे फ़ंक्शन जोड़ें जो खुद को कॉल करते हैं. अगर थोड़ी सावधानी बरती जाती है, तो इससे सेमेंटेक्स में कोई बदलाव नहीं होता और आपके फ़ंक्शन के कुछ हिस्से, प्रोफ़ाइलर में अलग-अलग फ़ंक्शन के तौर पर दिखते हैं: js function myLongFunction() { ... (function doAPartOfTheWork() { ... })(); ... } प्रोफ़ाइलिंग पूरी होने के बाद, इन अतिरिक्त फ़ंक्शन को हटाना न भूलें. इसके अलावा, अपने कोड को फिर से बनाने के लिए, इनका इस्तेमाल शुरुआती पॉइंट के तौर पर भी किया जा सकता है.

डीओएम प्रोफ़ाइलिंग

Chrome वेब इंस्पेक्टर के नए डेवलपमेंट टूल में, "टाइमलाइन व्यू" की सुविधा शामिल है. यह ब्राउज़र की लो लेवल ऐक्शन की टाइमलाइन दिखाती है. इस जानकारी का इस्तेमाल, अपने DOM ऑपरेशन को ऑप्टिमाइज़ करने के लिए किया जा सकता है. आपको यह कोशिश करनी चाहिए कि आपका कोड लागू होने के दौरान, ब्राउज़र को ज़्यादा "कार्रवाइयां" न करनी पड़ें.

टाइमलाइन व्यू से बहुत ज़्यादा जानकारी मिल सकती है. इसलिए, आपको कम से कम ऐसे टेस्ट केस बनाने चाहिए जिन्हें अलग से चलाया जा सके.

डीओएम प्रोफ़ाइलिंग

ऊपर दी गई इमेज में, किसी आसान स्क्रिप्ट के लिए टाइमलाइन व्यू का आउटपुट दिखाया गया है. बाईं ओर मौजूद पैनल में, ब्राउज़र के किए गए ऑपरेशन का क्रम दिखता है. वहीं, दाईं ओर मौजूद टाइमलाइन में, किसी ऑपरेशन में लगने वाला कुल समय दिखता है.

टाइमलाइन व्यू के बारे में ज़्यादा जानकारी. Internet Explorer में प्रोफ़ाइलिंग के लिए, DynaTrace Ajax Edition एक अन्य टूल है.

प्रोफ़ाइल बनाने की रणनीतियां

खास बातों को हाइलाइट करना

अपने ऐप्लिकेशन की प्रोफ़ाइल बनाने के लिए, उसकी मुख्य सुविधाओं को अलग-अलग करके देखें. इससे आपको पता चलेगा कि ऐप्लिकेशन में कौनसी सुविधाएं धीमी हैं. इसके बाद, प्रोफ़ाइल चलाने की कोशिश करें, जो आपके कोड के सिर्फ़ उन हिस्सों को चलाए जो आपके ऐप्लिकेशन के इन पहलुओं के लिए काम के हैं. इससे प्रोफ़ाइलिंग डेटा को समझना आसान हो जाएगा, क्योंकि इसे ऐसे कोड पाथ के साथ नहीं मिलाया जाता है जो आपकी असल समस्या से जुड़े नहीं होते. आपके आवेदन के अलग-अलग पहलुओं के लिए अच्छे उदाहरण ये हो सकते हैं:

  1. ऐप्लिकेशन शुरू होने में लगने वाला समय (प्रोफ़ाइलर को चालू करना, ऐप्लिकेशन को फिर से लोड करना, शुरू होने की प्रोसेस पूरी होने तक इंतज़ार करना, प्रोफ़ाइलर को बंद करना.
  2. किसी बटन और उसके बाद के ऐनिमेशन पर क्लिक करें (प्रोफ़ाइलर शुरू करें, बटन पर क्लिक करें, ऐनिमेशन पूरा होने तक इंतज़ार करें, प्रोफ़ाइलर बंद करें).
जीयूआई प्रोफ़ाइलिंग

अपने ऐप्लिकेशन के सिर्फ़ सही हिस्से को चलाना, जीयूआई प्रोग्राम में ज़्यादा मुश्किल हो सकता है. जैसे, अपने 3D इंजन के रे ट्रेसर को ऑप्टिमाइज़ करना. उदाहरण के लिए, जब आपको किसी बटन पर क्लिक करने पर होने वाली गतिविधियों की प्रोफ़ाइल बनानी हो, तो हो सकता है कि आप इस दौरान उससे जुड़े बिना किसी काम के माउसओवर इवेंट ट्रिगर कर दें. इससे आपके नतीजे कम सटीक हो सकते हैं. इससे बचने की कोशिश करें :)

प्रोग्रामैटिक इंटरफ़ेस

डीबगर को चालू करने के लिए, प्रोग्राम के हिसाब से इंटरफ़ेस भी उपलब्ध है. इससे, प्रोफ़ाइलिंग शुरू होने और खत्म होने के समय को सटीक तरीके से कंट्रोल किया जा सकता है.

प्रोफ़ाइल बनाने के लिए, इनमें से कोई एक तरीका अपनाएं:

console.profile()

इनके साथ प्रोफ़ाइलिंग बंद करें:

console.profileEnd()

रिपीटेबिलिटी

प्रोफ़ाइल बनाते समय, पक्का करें कि आपके पास अपने नतीजे फिर से पाने का विकल्प हो. इसके बाद ही, यह पता चल पाएगा कि आपके ऑप्टिमाइज़ेशन से असल में परफ़ॉर्मेंस बेहतर हुई है या नहीं. साथ ही, फ़ंक्शन लेवल की प्रोफ़ाइलिंग आपके पूरे कंप्यूटर के संदर्भ में की जाती है. यह कोई सटीक विज्ञान नहीं है. अलग-अलग प्रोफ़ाइल चलाने पर, आपके कंप्यूटर पर होने वाली कई अन्य चीज़ों का असर पड़ सकता है:

  1. आपके ऐप्लिकेशन में मौजूद कोई ऐसा टाइमर जो किसी दूसरे मेज़रमेंट के दौरान ट्रिगर होता है.
  2. कचरा इकट्ठा करने वाला मशीन काम करते हुए.
  3. आपके ब्राउज़र में मौजूद कोई दूसरा टैब, उसी ऑपरेटिंग थ्रेड में ज़्यादा काम कर रहा है.
  4. आपके कंप्यूटर पर कोई दूसरा प्रोग्राम, सीपीयू का इस्तेमाल कर रहा है. इस वजह से, आपका ऐप्लिकेशन धीमा हो रहा है.
  5. धरती के गुरुत्वाकर्षण क्षेत्र में अचानक होने वाले बदलाव.

एक ही कोड पाथ को एक प्रोफ़ाइलिंग सेशन में कई बार चलाना भी सही होता है. इस तरह, ऊपर बताई गई बातों का असर कम हो जाता है और वीडियो के धीमे हिस्से ज़्यादा साफ़ तौर पर दिखते हैं.

मेज़र करें, बेहतर बनाएं, फिर से मेज़र करें

अपने प्रोग्राम में किसी धीमे हिस्से की पहचान करने के बाद, उसे बेहतर बनाने के तरीके खोजें. कोड बदलने के बाद, प्रोफ़ाइल फिर से सेट अप करें. अगर आपको नतीजा पसंद आया है, तो आगे बढ़ें. अगर आपको कोई सुधार नहीं दिख रहा है, तो आपको अपने बदलाव को पहले जैसा कर देना चाहिए. इसे "इससे कोई नुकसान नहीं होगा" के मकसद से नहीं छोड़ना चाहिए.

ऑप्टिमाइज़ेशन कार्यनीतियां

DOM इंटरैक्शन को कम करना

वेब क्लाइंट ऐप्लिकेशन की स्पीड को बेहतर बनाने के लिए, डीओएम इंटरैक्शन को कम करना एक आम तरीका है. JavaScript इंजन की स्पीड काफ़ी बढ़ गई है, लेकिन DOM को ऐक्सेस करने की स्पीड उतनी नहीं बढ़ी है. यह भी बहुत ही व्यावहारिक वजहों से कभी नहीं होगा. जैसे, स्क्रीन पर लेआउट बनाने और ड्रॉ करने में समय लगता है.

डीओएम नोड को कैश मेमोरी में सेव करना

जब भी डीओएम से कोई नोड या नोड की सूची वापस ली जाती है, तो इस बारे में सोचें कि क्या बाद में होने वाले कैलकुलेशन में या अगले लूप में, उनका फिर से इस्तेमाल किया जा सकता है. जब तक काम के एरिया में कोई नोड नहीं जोड़ा जाता या मिटाया नहीं जाता, तब तक ऐसा होता है.

पहले:

function getElements() {
  return $('.my-class');
}

बाद में:

var cachedElements;
function getElements() {
  if (cachedElements) {
    return cachedElements;
  }
  cachedElements = $('.my-class');
  return cachedElements;
}

एट्रिब्यूट की वैल्यू कैश मेमोरी में सेव करना

जिस तरह DOM नोड को कैश मेमोरी में सेव किया जा सकता है उसी तरह एट्रिब्यूट की वैल्यू को भी कैश मेमोरी में सेव किया जा सकता है. मान लें कि आपको किसी नोड की स्टाइल के एट्रिब्यूट को ऐनिमेट करना है. अगर आपको पता है कि आप ही (जैसे कि कोड के उस हिस्से में) उस एट्रिब्यूट को कभी भी इस्तेमाल करेंगे, तो हर बार आखिरी वैल्यू को कैश मेमोरी में सेव किया जा सकता है, ताकि आपको उसे बार-बार न पढ़ना पड़े.

पहले:

setInterval(function() {
  var ele = $('#element');
  var left = parseInt(ele.css('left'), 10);
  ele.css('left', (left + 5) + 'px');
}, 1000 / 30);

इसके बाद: js var ele = $('#element'); var left = parseInt(ele.css('left'), 10); setInterval(function() { left += 5; ele.css('left', left + 'px'); }, 1000 / 30);

डीओएम में बदलाव करने की प्रोसेस को लूप से बाहर ले जाना

आम तौर पर, लूप को ऑप्टिमाइज़ करने के लिए ज़्यादा समय लगता है. असल संख्याओं को कैलकुलेट करने की प्रोसेस को DOM के साथ काम करने से अलग करने के तरीके खोजें. अक्सर, गणना करने के बाद, सभी नतीजों को एक साथ लागू किया जा सकता है.

पहले:

document.getElementById('target').innerHTML = '';
for(var i = 0; i < array.length; i++) {
  var val = doSomething(array[i]);
  document.getElementById('target').innerHTML += val;
}

बाद में:

var stringBuilder = [];
for(var i = 0; i < array.length; i++) {
  var val = doSomething(array[i]);
  stringBuilder.push(val);
}
document.getElementById('target').innerHTML = stringBuilder.join('');

फिर से ड्रॉ करना और फिर से फ़्लो करना

जैसा कि हमने पहले बताया है कि DOM को ऐक्सेस करने में ज़्यादा समय लगता है. जब आपका कोड ऐसी वैल्यू पढ़ रहा हो जिसकी फिर से गिनती करनी पड़े, तो यह बहुत धीमा हो जाता है. ऐसा इसलिए होता है, क्योंकि आपके कोड ने हाल ही में DOM से जुड़ी किसी चीज़ में बदलाव किया है. इसलिए, DOM के पढ़ने और लिखने के ऐक्सेस को एक साथ इस्तेमाल करने से बचना चाहिए. आम तौर पर, आपके कोड को हमेशा दो चरणों में बांटा जाना चाहिए:

  • पहला चरण: अपने कोड के लिए ज़रूरी DOM वैल्यू पढ़ना
  • दूसरा चरण: डीओएम में बदलाव करना

इस तरह के पैटर्न को प्रोग्राम न करें:

  • पहला चरण: डीओएम वैल्यू पढ़ना
  • दूसरा चरण: डीओएम में बदलाव करना
  • तीसरा चरण: ज़्यादा पढ़ें
  • चौथा चरण: किसी दूसरी जगह पर डीओएम में बदलाव करना.

पहले:

function paintSlow() {
  var left1 = $('#thing1').css('left');
  $('#otherThing1').css('left', left);
  var left2 = $('#thing2').css('left');
  $('#otherThing2').css('left', left);
}

बाद में:

function paintFast() {
  var left1 = $('#thing1').css('left');
  var left2 = $('#thing2').css('left');
  $('#otherThing1').css('left', left);
  $('#otherThing2').css('left', left);
}

इस सलाह को, एक JavaScript एक्सीक्यूशन कॉन्टेक्स्ट में होने वाली कार्रवाइयों के लिए ध्यान में रखा जाना चाहिए. (उदाहरण के लिए, किसी इवेंट हैंडलर में, किसी इंटरवल हैंडलर में या किसी Ajax रिस्पॉन्स को हैंडल करते समय.)

ऊपर दिए गए paintSlow() फ़ंक्शन को लागू करने पर, यह इमेज बनती है:

paintSlow()

तेज़ी से लागू करने की सुविधा पर स्विच करने पर, यह इमेज दिखती है:

तेज़ी से लागू होना

इन इमेज से पता चलता है कि आपके कोड के डीओएम को ऐक्सेस करने के तरीके को क्रम से बदलने से, रेंडर करने की परफ़ॉर्मेंस काफ़ी बेहतर हो सकती है. इस मामले में, ओरिजनल कोड को एक ही नतीजा पाने के लिए, स्टाइल और पेज के लेआउट का दोबारा हिसाब लगाना पड़ता है. इसी तरह का ऑप्टिमाइज़ेशन, असल ज़िंदगी से जुड़े सभी कोड पर लागू किया जा सकता है. इससे बेहतर नतीजे मिल सकते हैं.

ज़्यादा पढ़ें: रेंडरिंग: फिर से रंगना, फिर से फ़्लो/फिर से लेआउट करना, फिर से स्टाइल करना, स्टोयन स्टेफ़नोव की लेख

फिर से ड्रॉ करना और इवेंट लूप

ब्राउज़र में JavaScript को चलाने के लिए, "इवेंट लूप" मॉडल का इस्तेमाल किया जाता है. डिफ़ॉल्ट रूप से, ब्राउज़र "इडल" मोड में होता है. इस स्थिति में, उपयोगकर्ता इंटरैक्शन या JavaScript टाइमर या Ajax कॉलबैक जैसी चीज़ों से रुकावट आ सकती है. जब भी किसी रुकावट वाले पॉइंट पर JavaScript का कोई हिस्सा चलता है, तो ब्राउज़र आम तौर पर तब तक इंतज़ार करता है, जब तक स्क्रीन फिर से नहीं दिखती. हालांकि, बहुत लंबे समय तक चलने वाले JavaScript या सूचना बॉक्स जैसे मामलों में, JavaScript के एक्ज़ीक्यूशन में रुकावट आ सकती है.

नतीजे

  1. अगर आपके JavaScript ऐनिमेशन साइकल को लागू होने में 1/30 सेकंड से ज़्यादा समय लगता है, तो आपको स्मूद ऐनिमेशन नहीं मिलेंगे. इसकी वजह यह है कि JS लागू होने के दौरान, ब्राउज़र फिर से पेज को रीपेंट नहीं करेगा. अगर आपको उपयोगकर्ता इवेंट भी हैंडल करने हैं, तो आपको ज़्यादा तेज़ी से काम करना होगा.
  2. कभी-कभी कुछ JavaScript कार्रवाइयों को थोड़ी देर के लिए रोकना काम का होता है. उदाहरण के लिए, setTimeout(function() { ... }, 0) इससे ब्राउज़र को यह निर्देश मिलता है कि इवेंट लूप फिर से निष्क्रिय होने के बाद, कॉलबैक को तुरंत लागू करें. कुछ ब्राउज़र कम से कम 10 मिलीसेकंड तक इंतज़ार करेंगे. आपको यह ध्यान रखना होगा कि इससे JavaScript को दो बार चलाने के साइकल बनेंगे, जो समय के हिसाब से एक-दूसरे के बहुत करीब होंगे. दोनों ही स्थितियों में, स्क्रीन को फिर से पेंट करने की प्रोसेस शुरू हो सकती है. इससे, पेंट करने में लगने वाला कुल समय दोगुना हो सकता है. यह असल में दो पेन्ट ट्रिगर करता है या नहीं, यह ब्राउज़र में मौजूद हेयुरिस्टिक्स पर निर्भर करता है.

सामान्य वर्शन:

function paintFast() {
  var height1 = $('#thing1').css('height');
  var height2 = $('#thing2').css('height');
  $('#otherThing1').css('height', '20px');
  $('#otherThing2').css('height', '20px');
}
फिर से ड्रॉ करना और इवेंट लूप

चलिए, कुछ देरी जोड़ते हैं:

function paintALittleLater() {
  var height1 = $('#thing1').css('height');
  var height2 = $('#thing2').css('height');
  $('#otherThing1').css('height', '20px');
  setTimeout(function() {
    $('#otherThing2').css('height', '20px');
  }, 10)
}
देरी

देर से लोड होने वाले वर्शन से पता चलता है कि ब्राउज़र पेज को दो बार पेंट करता है. हालांकि, पेज में किए गए दोनों बदलाव, एक सेकंड के 1/100 हिस्से के बराबर हैं.

लेज़ी इनिशलाइज़ेशन

उपयोगकर्ता ऐसे वेब ऐप्लिकेशन चाहते हैं जो तेज़ी से लोड हों और रिस्पॉन्सिव हों. हालांकि, उपयोगकर्ताओं के हिसाब से, ऐप्लिकेशन के धीमे होने की सीमा अलग-अलग होती है. यह इस बात पर निर्भर करता है कि उपयोगकर्ता क्या कार्रवाई कर रहा है.उदाहरण के लिए, किसी ऐप्लिकेशन को माउस घुमाने पर, बहुत ज़्यादा कैलकुलेशन नहीं करना चाहिए. ऐसा करने से, उपयोगकर्ता को खराब अनुभव मिल सकता है. हालांकि, उपयोगकर्ताओं को बटन पर क्लिक करने के बाद, थोड़ी देरी का इंतज़ार करना पड़ता है.

इसलिए, अपने शुरुआती कोड को ज़रूरत पड़ने पर ही चलाएं. उदाहरण के लिए, जब उपयोगकर्ता आपके ऐप्लिकेशन के किसी खास कॉम्पोनेंट को चालू करने वाले बटन पर क्लिक करता है.

पहले: js var things = $('.ele > .other * div.className'); $('#button').click(function() { things.show() });

इसके बाद: js $('#button').click(function() { $('.ele > .other * div.className').show() });

इवेंट का ऐक्सेस देना

इवेंट हैंडलर को पूरे पेज पर फैलाने में ज़्यादा समय लग सकता है. साथ ही, एलिमेंट डाइनैमिक तौर पर बदलने के बाद, इवेंट हैंडलर को नए एलिमेंट से फिर से जोड़ना भी मुश्किल हो सकता है.

इस मामले में, इवेंट डिलीगेशन नाम की तकनीक का इस्तेमाल किया जा सकता है. एलिमेंट में अलग-अलग इवेंट हैंडलर अटैच करने के बजाय, कई ब्राउज़र इवेंट के बबल होने की सुविधा का इस्तेमाल किया जाता है. इसके लिए, इवेंट हैंडलर को पैरंट नोड से अटैच किया जाता है और इवेंट के टारगेट नोड की जांच की जाती है, ताकि यह पता लगाया जा सके कि इवेंट काम का है या नहीं.

jQuery में इसे आसानी से इस तरह दिखाया जा सकता है:

$('#parentNode').delegate('.button', 'click', function() { ... });

इवेंट डीलेगेशन का इस्तेमाल कब नहीं करना चाहिए

कभी-कभी ऐसा भी हो सकता है कि इवेंट डेलिगेशन का इस्तेमाल करने पर, आपको परफ़ॉर्मेंस से जुड़ी समस्या आ रही हो. इवेंट को किसी दूसरे को असाइन करने की सुविधा से, इवेंट को शुरू करने में लगने वाला समय कम हो जाता है. हालांकि, किसी इवेंट के ट्रिगर होने पर, यह जांचने के लिए शुल्क देना पड़ता है कि वह इवेंट आपके काम का है या नहीं. ऐसा करने पर, खास तौर पर "mouseover" या "mousemove" जैसे बार-बार होने वाले इवेंट के लिए, ज़्यादा लागत आ सकती है.

आम समस्याएं और उनके समाधान

$(document).ready में मेरे काम में बहुत समय लगता है

माल्टे की निजी सलाह: $(document).ready में कभी भी कुछ न करें. अपने दस्तावेज़ को फ़ाइनल फ़ॉर्मैट में डिलीवर करने की कोशिश करें. ठीक है, आपके पास इवेंट लिसनर रजिस्टर करने की अनुमति है. हालांकि, इवेंट लिसनर को सिर्फ़ आईडी-सिलेक्टर और/या इवेंट डीलेगेशन का इस्तेमाल करके रजिस्टर किया जा सकता है. "mousemove" जैसे महंगे इवेंट के लिए, रजिस्टर करने की प्रोसेस को तब तक रोकें, जब तक कि इवेंट की ज़रूरत न पड़े. जैसे, काम के एलिमेंट पर कर्सर घुमाने पर होने वाला इवेंट.

अगर आपको असल डेटा पाने के लिए Ajax अनुरोध करना है, तो कोई अच्छा ऐनिमेशन दिखाएं. अगर यह ऐनिमेट किया गया GIF या इससे मिलता-जुलता कोई ऐनिमेशन है, तो हो सकता है कि आप उसे डेटा यूआरआई के तौर पर शामिल करना चाहें.

पेज पर फ़्लैश मूवी जोड़ने के बाद, सब कुछ बहुत धीमा हो गया है

किसी पेज में फ़्लैश जोड़ने से, रेंडरिंग की प्रोसेस थोड़ी धीमी हो जाती है. इसकी वजह यह है कि विंडो के फ़ाइनल लेआउट को ब्राउज़र और फ़्लैश प्लग इन के बीच "बातचीत" करनी होती है. अगर आपके पेजों पर फ़्लैश का इस्तेमाल करना ज़रूरी है, तो पक्का करें कि आपने "wmode" फ़्लैश-पैरामीटर को "window" वैल्यू पर सेट किया हो. यह वैल्यू डिफ़ॉल्ट होती है. इससे, एचटीएमएल और फ़्लैश एलिमेंट को कंपोज करने की सुविधा बंद हो जाएगी. साथ ही, आपको फ़्लैश फ़िल्म के ऊपर मौजूद एचटीएमएल एलिमेंट नहीं दिखेगा और आपकी फ़्लैश फ़िल्म पारदर्शी नहीं हो पाएगी. इससे आपको परेशानी हो सकती है, लेकिन इससे आपकी परफ़ॉर्मेंस काफ़ी बेहतर होगी. उदाहरण के लिए, देखें कि youtube.com, मुख्य फ़िल्म प्लेयर के ऊपर लेयर डालने से कैसे बचता है.

मैं localStorage में चीज़ें सेव कर रहा/रही हूं, अब मेरा ऐप्लिकेशन रुक-रुककर चल रहा है

localStorage में डेटा सेव करना, सिंक होने वाला एक ऑपरेशन है. इसमें आपकी हार्ड डिस्क को स्पिन करना शामिल है. ऐनिमेशन करते समय, "लंबे समय तक चलने वाले" सिंक्रोनस ऑपरेशन कभी नहीं करने चाहिए. localStorage को ऐक्सेस करने की सुविधा को अपने कोड में उस जगह पर ले जाएं जहां आपको पता हो कि उपयोगकर्ता कोई काम नहीं कर रहा है और कोई ऐनिमेशन नहीं चल रहा है.

प्रोफ़ाइलिंग से पता चलता है कि jQuery सिलेक्टर बहुत धीमा है

सबसे पहले, आपको यह पक्का करना होगा कि आपका सिलेक्टर document.querySelectorAll के ज़रिए चलाया जा सकता है. इसकी जांच, JavaScript कंसोल में की जा सकती है. अगर कोई अपवाद है, तो अपने JavaScript फ़्रेमवर्क के किसी भी खास एक्सटेंशन का इस्तेमाल न करने के लिए, अपना सिलेक्टर फिर से लिखें. इससे मॉडर्न ब्राउज़र में, आपके सिलेक्टर की स्पीड बहुत ज़्यादा बढ़ जाएगी.

अगर इससे मदद नहीं मिलती है या आपको मॉडर्न ब्राउज़र में भी तेज़ी से काम करना है, तो इन दिशा-निर्देशों का पालन करें:

  • अपने सिलेक्टर की दाईं ओर, ज़्यादा से ज़्यादा सटीक जानकारी दें.
  • सबसे दाईं ओर मौजूद सिलेक्टर के हिस्से के तौर पर, किसी ऐसे टैग के नाम का इस्तेमाल करें जिसका इस्तेमाल अक्सर न किया जाता हो.
  • अगर कोई भी तरीका काम न करे, तो चीज़ों को फिर से लिखने के बारे में सोचें, ताकि आप आईडी-सिलेक्टर का इस्तेमाल कर सकें

डीओएम में इन सभी बदलावों में काफ़ी समय लगता है

डीओएम नोड को एक साथ डालने, हटाने, और अपडेट करने की प्रोसेस बहुत धीमी हो सकती है. आम तौर पर, एचटीएमएल की बड़ी स्ट्रिंग जनरेट करके और पुराने कॉन्टेंट को बदलने के लिए domNode.innerHTML = newHTML का इस्तेमाल करके, इसे ऑप्टिमाइज़ किया जा सकता है. ध्यान दें कि यह रखरखाव के लिए बहुत खराब हो सकता है और IE में मेमोरी लिंक बना सकता है. इसलिए, सावधान रहें.

एक और आम समस्या यह है कि आपका शुरुआती कोड बहुत सारा एचटीएमएल बना सकता है. उदाहरण के लिए, कोई jQuery प्लग इन, जो किसी चुनिंदा बॉक्स को कई डाइव में बदल देता है, क्योंकि डिज़ाइनर को UX के सबसे सही तरीकों के बारे में जानकारी नहीं है. अगर आपको अपने पेज को तेज़ी से लोड करना है, तो ऐसा कभी न करें. इसके बजाय, सर्वर साइड से सभी मार्कअप को उसके फ़ाइनल फ़ॉर्म में डिलीवर करें. इसमें भी कई समस्याएं हैं. इसलिए, अच्छी तरह से सोच-विचार करके तय करें कि क्या गति के लिए इन समस्याओं को स्वीकार किया जा सकता है.

टूल

  1. JSPerf - JavaScript के छोटे स्निपेट का बेंचमार्क
  2. Firebug - Firefox में प्रोफ़ाइलिंग के लिए
  3. Google Chrome डेवलपर टूल (Safari में WebInspector के तौर पर उपलब्ध है)
  4. DOM मॉन्स्टर - डीओएम की परफ़ॉर्मेंस को ऑप्टिमाइज़ करने के लिए
  5. DynaTrace Ajax Edition - Internet Explorer में प्रोफ़ाइलिंग और पेंट ऑप्टिमाइज़ेशन के लिए

इसके बारे में और पढ़ें

  1. Google Speed
  2. Paul Irish on jQuery Performance
  3. बेहतरीन JavaScript परफ़ॉर्मेंस (स्लाइड डेक)