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

शुरुआती जानकारी

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

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

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

रणनीति

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

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

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

हार्डवेयर त्वरण

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

जीपीयू की मदद से, दस्तावेज़ की इन चीज़ों की रफ़्तार बढ़ाई जा सकती है

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

कैनवस की रफ़्तार बढ़ाना और WebGL

किस चीज़ को तेज़ किया जा सकता है?

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

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

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

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

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

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

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

CSS3 ट्रांज़िशन

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

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

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

CSS3 अनुवाद

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

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

<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>

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

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

window.requestAnimationFrame

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

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

प्रोफ़ाइल बनाई जा रही है

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

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

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

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

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

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

डीओएम

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

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() { ... })(); ... } प्रोफ़ाइलिंग हो जाने के बाद, इन अतिरिक्त फ़ंक्शन को हटाना न भूलें या यहां तक कि अपने कोड की रीफ़ैक्टर करने के लिए, इनका इस्तेमाल शुरुआत की जगह के तौर पर करें.

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

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

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

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

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

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

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

सिंगल-आउट आसपेक्ट रेशियो (लंबाई-चौड़ाई का अनुपात)

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

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

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

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

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

इनके साथ प्रोफ़ाइल बनाना शुरू करें:

console.profile()

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

console.profileEnd()

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

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

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

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

मापें, सुधार करें, आकलन करें

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

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

डीओएम इंटरैक्शन को छोटा करें

वेब क्लाइंट ऐप्लिकेशन की स्पीड को बेहतर बनाने की एक सामान्य थीम है, डीओएम इंटरैक्शन को कम करना. हालांकि, 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 में बदलाव करें
  • तीसरा चरण: कुछ और पढ़ें
  • चरण 4: 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 के काम न करने वाले अलर्ट-बॉक्स के मामले में अपवाद हो सकते हैं, जो 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() { ... });

इवेंट का ऐक्सेस देने की सुविधा का इस्तेमाल कब नहीं करना चाहिए

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

सामान्य समस्याएं और समाधान

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

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

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

जब से मैंने पेज पर एक फ़्लैश फ़िल्म जोड़ी है, सब कुछ बहुत धीमा चल रहा है

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

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

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

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

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

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

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

इन सभी DOM मैनिप्यूलेशन में ज़्यादा समय लगता है

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

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

टूल

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

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

  1. Google स्पीड
  2. jQuery Performance के बारे में पॉल आयरिश
  3. बहुत ज़्यादा JavaScript परफ़ॉर्मेंस (स्लाइड डेक)