AngularJS
प्रोफाइलिंग और प्रदर्शन
खोज…
7 सरल प्रदर्शन में सुधार
1) एनजी-दोहराएँ का उपयोग संयम से करें
दृश्यों में ng-repeat
का उपयोग करने से आमतौर पर खराब प्रदर्शन होता है, खासकर जब नेस्टेड ng-repeat
।
यह सुपर स्लो है!
<div ng-repeat="user in userCollection">
<div ng-repeat="details in user">
{{details}}
</div>
</div>
जितना हो सके नेस्टेड रिपीट से बचने की कोशिश करें। ng-repeat
के प्रदर्शन को बेहतर बनाने का एक तरीका है track by $index
का उपयोग track by $index
(या किसी अन्य आईडी फील्ड) द्वारा किया जाना। डिफ़ॉल्ट रूप से, ng-repeat
पूरे ऑब्जेक्ट ng-repeat
ट्रैक करता है। track by
साथ, कोणीय केवल वस्तु को $index
या ऑब्जेक्ट आईडी द्वारा देखता है।
<div ng-repeat="user in userCollection track by $index">
{{user.data}}
</div>
पृष्ठांकन , वर्चुअल स्क्रॉल , अनंत स्क्रॉल या सीमा जैसे अन्य दृष्टिकोण का उपयोग करें : जब भी संभव हो, बड़े संग्रह पर पुनरावृत्ति से बचने के लिए शुरू करें ।
2) एक बार बांधें
कोणीय में द्विदिश डेटा बाइंडिंग है। यह बहुत अधिक उपयोग किए जाने पर धीमी होने की लागत के साथ आता है।
धीमा प्रदर्शन
<!-- Default data binding has a performance cost -->
<div>{{ my.data }}</div>
तेज़ प्रदर्शन (AngularJS> = 1.3)
<!-- Bind once is much faster -->
<div>{{ ::my.data }}</div>
<div ng-bind="::my.data"></div>
<!-- Use single binding notation in ng-repeat where only list display is needed -->
<div ng-repeat="user in ::userCollection">
{{::user.data}}
</div>
"बाइंड एक बार" संकेतन का उपयोग करना, कोणीय को पाचन चक्र की पहली श्रृंखला के बाद मूल्य के लिए प्रतीक्षा करने के लिए कहता है। कोणीय डोम में उस मान का उपयोग करेगा, फिर सभी पहरेदारों को हटा देगा ताकि यह एक स्थिर मूल्य बन जाए और अब मॉडल के लिए बाध्य न हो।
{{}}
बहुत धीमी है।
यह ng-bind
एक निर्देश है और पारित चर पर एक द्रष्टा को रखेगा। तो ng-bind
केवल तभी लागू होगा, जब पारित मूल्य वास्तव में बदलता है।
दूसरी ओर कोष्ठकों को हर $digest
में गंदा जाँच और ताज़ा किया जाएगा, भले ही यह आवश्यक न हो।
3) स्कोप फंक्शन और फिल्टर में समय लगता है
AngularJS में डाइजेस्ट लूप होता है। आपके सभी कार्य एक दृश्य में होते हैं और हर बार पाचन चक्र चलने पर फ़िल्टर निष्पादित होते हैं। जब भी मॉडल को अपडेट किया जाता है तो यह डाइजेस्ट लूप निष्पादित किया जाएगा और यह आपके ऐप को धीमा कर सकता है (पेज लोड होने से पहले कई बार फ़िल्टर हिट किया जा सकता है)।
इससे बचें:
<div ng-controller="bigCalulations as calc">
<p>{{calc.calculateMe()}}</p>
<p>{{calc.data | heavyFilter}}</p>
</div>
बेहतर दृष्टिकोण
<div ng-controller="bigCalulations as calc">
<p>{{calc.preCalculatedValue}}</p>
<p>{{calc.data | lightFilter}}</p>
</div>
नियंत्रक कहां हो सकता है:
app.controller('bigCalulations', function(valueService) {
// bad, because this is called in every digest loop
this.calculateMe = function() {
var t = 0;
for(i = 0; i < 1000; i++) {
t += i;
}
return t;
}
// good, because this is executed just once and logic is separated in service to keep the controller light
this.preCalulatedValue = valueService.valueCalculation(); // returns 499500
});
4 पहरेदार
दर्शकों ने जबरदस्त प्रदर्शन छोड़ दिया। अधिक देखने वालों के साथ, डाइजेस्ट लूप को अधिक समय लगेगा और UI धीमा हो जाएगा। यदि द्रष्टा परिवर्तन का पता लगाता है, तो यह डाइजेस्ट लूप को किक करेगा और दृश्य को फिर से प्रस्तुत करेगा।
कोणीय में परिवर्तनशील परिवर्तन के लिए मैनुअल देखने के तीन तरीके हैं।
$watch()
- मूल्य परिवर्तन के लिए देखता है
$watchCollection()
- संग्रह में परिवर्तन के लिए देखता है (नियमित $watch
से अधिक देखता है)
$watch(..., true)
- जितना संभव हो इससे बचें , यह "गहरी घड़ी" का प्रदर्शन करेगा और प्रदर्शन को कम कर देगा ( watchCollection
से अधिक देखता है)
ध्यान दें कि यदि आप दृश्य में वैरिएबल बाँध रहे हैं तो आप नई घड़ियाँ बना रहे हैं - विशेष रूप से छोरों में, एक घड़ी बनाने से रोकने के लिए {{::variable}}
का उपयोग करें।
परिणामस्वरूप आपको यह ट्रैक करने की आवश्यकता है कि आप कितने वॉचर्स का उपयोग कर रहे हैं। आप इस स्क्रिप्ट के साथ वॉचर्स की गिनती कर सकते हैं ( क्रेडिट्स @ जेरेड लाइक जारेड नंबर ऑफ वॉचर्स )
(function() {
var root = angular.element(document.getElementsByTagName('body')),
watchers = [],
f = function(element) {
angular.forEach(['$scope', '$isolateScope'], function(scopeProperty) {
if(element.data() && element.data().hasOwnProperty(scopeProperty)) {
angular.forEach(element.data()[scopeProperty].$$watchers, function(watcher) {
watchers.push(watcher);
});
}
});
angular.forEach(element.children(), function(childElement) {
f(angular.element(childElement));
});
};
f(root);
// Remove duplicate watchers
var watchersWithoutDuplicates = [];
angular.forEach(watchers, function(item) {
if(watchersWithoutDuplicates.indexOf(item) < 0) {
watchersWithoutDuplicates.push(item);
}
});
console.log(watchersWithoutDuplicates.length);
})();
5) एनजी-अगर / एनजी-शो
ये कार्य व्यवहार में बहुत समान हैं। ng-if
डोम से तत्वों को हटाता है जबकि ng-show
केवल तत्वों को छुपाता है लेकिन सभी हैंडलर रखता है। यदि आपके पास उस कोड के कुछ भाग हैं जिन्हें आप दिखाना नहीं चाहते हैं, ng-if
उपयोग करें।
यह उपयोग के प्रकार पर निर्भर करता है, लेकिन अक्सर एक दूसरे की तुलना में अधिक उपयुक्त होता है।
यदि तत्व की आवश्यकता नहीं है,
ng-if
उपयोग करेंजल्दी से चालू / बंद करने के लिए,
ng-show/ng-hide
<div ng-repeat="user in userCollection"> <p ng-if="user.hasTreeLegs">I am special<!-- some complicated DOM --></p> <p ng-show="user.hasSubscribed">I am awesome<!-- switch this setting on and off --></p> </div>
यदि संदेह में - ng-if
इफ और परीक्षण ng-if
उपयोग करें!
6) डिबगिंग अक्षम करें
डिफ़ॉल्ट रूप से, विभिन्न डिबगिंग टूलों की सहायता के लिए कोड में अतिरिक्त कक्षाएं और मार्कअप को बांधने के निर्देश और स्कूप बांधते हैं। इस विकल्प को अक्षम करने का अर्थ है कि अब आप इन विभिन्न तत्वों को पाचन चक्र के दौरान प्रस्तुत नहीं करते हैं।
angular.module('exampleApp', []).config(['$compileProvider', function ($compileProvider) {
$compileProvider.debugInfoEnabled(false);
}]);
7) अपने संसाधनों को उजागर करने के लिए निर्भरता इंजेक्शन का उपयोग करें
डिपेंडेंसी इंजेक्शन एक सॉफ्टवेयर डिज़ाइन पैटर्न है जिसमें किसी ऑब्जेक्ट को उसकी निर्भरता दी जाती है, न कि ऑब्जेक्ट को खुद बनाने के लिए। यह हार्ड-कोडेड निर्भरता को दूर करने और जब भी जरूरत हो, उन्हें बदलना संभव बनाने के बारे में है।
आप सभी इंजेक्शन कार्यों के ऐसे स्ट्रिंग पार्सिंग से जुड़े प्रदर्शन लागत के बारे में आश्चर्यचकित हो सकते हैं। कोणीय पहली बार के बाद $ इंजेक्शन संपत्ति को कैशिंग द्वारा इसका ख्याल रखता है। तो ऐसा हर बार नहीं होता है कि किसी फ़ंक्शन को लागू करने की आवश्यकता होती है।
प्रो टिप: यदि आप सर्वश्रेष्ठ प्रदर्शन के साथ दृष्टिकोण की तलाश कर रहे हैं, तो $ इंजेक्शन संपत्ति एनोटेशन दृष्टिकोण के साथ जाएं। यह दृष्टिकोण पूरी तरह से फ़ंक्शन परिभाषा पार्सिंग से बचा जाता है क्योंकि यह तर्क एनोटेट फ़ंक्शन में निम्नलिखित चेक के भीतर लपेटा जाता है: यदि ((($ इंजेक्शन = fn। $ इंजेक्शन))। यदि $ इंजेक्शन पहले से उपलब्ध है, तो किसी भी पार्सिंग की आवश्यकता नहीं है!
var app = angular.module('DemoApp', []);
var DemoController = function (s, h) {
h.get('https://api.github.com/users/angular/repos').success(function (repos) {
s.repos = repos;
});
}
// $inject property annotation
DemoController['$inject'] = ['$scope', '$http'];
app.controller('DemoController', DemoController);
PRO TIP 2: आप सख्त डि मोड में चयन करने के लिए ng-app
के समान तत्व पर ng-strict-di
निर्देश जोड़ सकते हैं जो कि जब भी कोई सेवा निहित एनोटेशन का उपयोग करने की कोशिश करती है, तो एक त्रुटि होगी। उदाहरण:
<html ng-app="DemoApp" ng-strict-di>
या यदि आप मैन्युअल बूटस्ट्रैपिंग का उपयोग करते हैं:
angular.bootstrap(document, ['DemoApp'], {
strictDi: true
});
एक बार बाँध दो
कोणीय में अप्रत्यक्ष डेटा बाइंडिंग के लिए प्रतिष्ठा है। डिफ़ॉल्ट रूप से, कोणीय लगातार मॉडल और व्यू घटकों के बीच बंधे मूल्यों को सिंक्रनाइज़ करता है या तो मॉडल या व्यू घटक में किसी भी समय डेटा परिवर्तन होता है।
यह थोड़ा धीमा होने की लागत के साथ आता है अगर बहुत अधिक उपयोग किया जाता है। यह एक बड़ा प्रदर्शन हिट होगा:
खराब प्रदर्शन: {{my.data}}
दो कोलन जोड़े ::
चर नाम एक बार बंधन का उपयोग करने से पहले। इस स्थिति में, मान केवल एक बार अद्यतन हो जाता है जब my.data परिभाषित किया जाता है। आप स्पष्ट रूप से डेटा परिवर्तनों के लिए नहीं देखने का संकेत दे रहे हैं। कोणीय किसी भी मूल्य की जांच नहीं करेगा, जिसके परिणामस्वरूप प्रत्येक पाचन चक्र पर कम अभिव्यक्तियों का मूल्यांकन किया जाएगा।
एक बार के बंधन का उपयोग करके अच्छा प्रदर्शन उदाहरण
{{::my.data}}
<span ng-bind="::my.data"></span>
<span ng-if="::my.data"></span>
<span ng-repeat="item in ::my.data">{{item}}</span>
<span ng-class="::{ 'my-class': my.data }"></div>
नोट: यह हालांकि my.data
लिए द्वि-दिशात्मक डेटा बाइंडिंग को हटा देता है, इसलिए जब भी यह फ़ील्ड आपके एप्लिकेशन में my.data
, तो यह स्वचालित रूप से दृश्य में परिलक्षित नहीं होगी। तो इसका उपयोग केवल उन मूल्यों के लिए करें जो आपके आवेदन के पूरे जीवनकाल में नहीं बदलेंगे ।
स्कोप फ़ंक्शन और फ़िल्टर
AngularJS में लूप डाइजेस्ट होता है और आपके सभी फंक्शन एक फिल्टर में और हर बार डाइजेस्ट साइकिल के चलने पर फिल्टर निष्पादित होते हैं। जब भी मॉडल को अपडेट किया जाता है तो यह डाइजेस्ट लूप निष्पादित किया जाएगा और यह आपके ऐप को धीमा कर सकता है (पेज लोड होने से पहले कई बार फ़िल्टर हिट किया जा सकता है)।
आपको इससे बचना चाहिए:
<div ng-controller="bigCalulations as calc">
<p>{{calc.calculateMe()}}</p>
<p>{{calc.data | heavyFilter}}</p>
</div>
बेहतर दृष्टिकोण
<div ng-controller="bigCalulations as calc">
<p>{{calc.preCalculatedValue}}</p>
<p>{{calc.data | lightFilter}}</p>
</div>
नियंत्रक नमूना कहां है:
.controller("bigCalulations", function(valueService) {
// bad, because this is called in every digest loop
this.calculateMe = function() {
var t = 0;
for(i = 0; i < 1000; i++) {
t = t + i;
}
return t;
}
//good, because it is executed just once and logic is separated in service to keep the controller light
this.preCalulatedValue = valueService.caluclateSumm(); // returns 499500
});
पर नजर रखने वालों
वॉचर्स को कुछ मूल्य देखने के लिए आवश्यक है और पता लगाते हैं कि यह मान बदल गया है।
कॉल $watch()
या $watchCollection
नया वॉचर वर्तमान दायरे में आंतरिक वॉचर संग्रह में जुड़ जाता है।
तो, द्रष्टा क्या है?
द्रष्टा एक सरल कार्य है, जिसे हर पाचन चक्र पर कहा जाता है, और कुछ मूल्य देता है। कोणीय लौटाए गए मान की जांच करता है, यदि यह पिछले कॉल पर समान नहीं है - एक कॉलबैक जो $watch()
या $watchCollection
को कार्य करने के लिए दूसरे पैरामीटर में पारित किया गया था।
(function() {
angular.module("app", []).controller("ctrl", function($scope) {
$scope.value = 10;
$scope.$watch(
function() { return $scope.value; },
function() { console.log("value changed"); }
);
}
})();
वॉचर्स प्रदर्शन हत्यारे हैं। आपके पास जितने अधिक वॉचर्स हैं, वे पचाने वाले लूप बनाने में अधिक समय लेते हैं, धीमा UI। यदि कोई द्रष्टा परिवर्तन का पता लगाता है, तो यह डाइजेस्ट लूप को बंद कर देगा (सभी स्क्रीन पर पुनर्गणना)
एंगुलर में परिवर्तनशील परिवर्तन के लिए मैनुअल वॉच करने के तीन तरीके हैं।
$watch()
- केवल मूल्य परिवर्तन के लिए देखता है
$watchCollection()
- संग्रह में परिवर्तन के लिए देखता है (नियमित $ घड़ी से अधिक देखता है)
$watch(..., true)
- जितना संभव हो इससे बचें , यह "गहरी घड़ी" का प्रदर्शन करेगा और प्रदर्शन को मार देगा (वॉचकोल से अधिक घड़ियों)
ध्यान दें कि यदि आप दृश्य में वैरिएबल बाइंड कर रहे हैं, तो आप नए वॉचर्स बना रहे हैं - वॉच बनाने के लिए {{::variable}}
न करें, खासकर लूप्स में
परिणामस्वरूप आपको यह ट्रैक करने की आवश्यकता है कि आप कितने वॉचर्स का उपयोग कर रहे हैं। आप इस स्क्रिप्ट के साथ वॉचर्स की गिनती कर सकते हैं (क्रेडिट @ @वर्ड्स जैसे जारेड - पृष्ठ पर कुल घड़ियों की संख्या की गणना कैसे करें?
(function() {
var root = angular.element(document.getElementsByTagName("body")),
watchers = [];
var f = function(element) {
angular.forEach(["$scope", "$isolateScope"], function(scopeProperty) {
if(element.data() && element.data().hasOwnProperty(scopeProperty)) {
angular.forEach(element.data()[scopeProperty].$$watchers, function(watcher) {
watchers.push(watcher);
});
}
});
angular.forEach(element.children(), function(childElement) {
f(angular.element(childElement));
});
};
f(root);
// Remove duplicate watchers
var watchersWithoutDuplicates = [];
angular.forEach(watchers, function(item) {
if(watchersWithoutDuplicates.indexOf(item) < 0) {
watchersWithoutDuplicates.push(item);
}
});
console.log(watchersWithoutDuplicates.length);
})();
यदि आप अपनी स्क्रिप्ट नहीं बनाना चाहते हैं, तो एनजी-आँकड़े नामक एक ओपन सोर्स यूटिलिटी है जो पेज में एम्बेड किए गए वास्तविक समय चार्ट का उपयोग करता है ताकि आपको घड़ियों की संख्या की जानकारी मिल सके। समय के साथ पाचन चक्र की आवृत्ति और अवधि। उपयोगिता showAngularStats
नामक एक वैश्विक फ़ंक्शन को उजागर करती है जिसे आप यह कॉन्फ़िगर करने के लिए कॉल कर सकते हैं कि आप चार्ट कैसे काम करना चाहते हैं।
showAngularStats({
"position": "topleft",
"digestTimeThreshold": 16,
"autoload": true,
"logDigest": true,
"logWatches": true
});
उपरोक्त उदाहरण कोड स्वचालित रूप से पृष्ठ पर निम्नलिखित चार्ट को प्रदर्शित करता है ( इंटरैक्टिव डेमो )।
ng-if बनाम एनजी-शो
ये कार्य व्यवहार में बहुत समान हैं। अंतर यह है कि ng-if
डोम से तत्वों को हटाता है। यदि कोड के बड़े हिस्से हैं जो नहीं दिखाए जाएंगे, तो ng-if
जाने का रास्ता है। ng-show
केवल तत्वों को छिपाएगा लेकिन सभी हैंडलर रखेगा।
एनजी-अगर
NgIf निर्देश एक अभिव्यक्ति के आधार पर DOM ट्री के एक हिस्से को हटाता है या पुन: बनाता है। यदि एनजीआईएफ को सौंपी गई अभिव्यक्ति गलत मान का मूल्यांकन करती है तो तत्व को DOM से हटा दिया जाता है, अन्यथा तत्व का क्लोन DOM में फिर से स्थापित हो जाता है।
एनजी शो
NgShow निर्देश ngShow विशेषता को प्रदान की गई अभिव्यक्ति के आधार पर दिए गए HTML तत्व को दिखाता या छिपाता है। तत्व पर एनजी-छिपाने सीएसएस वर्ग को हटाने या जोड़कर तत्व को दिखाया या छिपाया गया है।
उदाहरण
<div ng-repeat="user in userCollection">
<p ng-if="user.hasTreeLegs">I am special
<!-- some complicated DOM -->
</p>
<p ng-show="user.hasSubscribed">I am aweosme
<!-- switch this setting on and off -->
</p>
</div>
निष्कर्ष
यह उपयोग के प्रकार से निर्भर करता है, लेकिन अक्सर एक दूसरे की तुलना में अधिक उपयुक्त होता है (उदाहरण के लिए, यदि समय की 95% तत्व की आवश्यकता नहीं है, ng-if
उपयोग करें; यदि आपको डोम तत्व की दृश्यता को टॉगल करने की आवश्यकता है, तो ng-if
का उपयोग करें ng-show
)।
जब संदेह में, ng-if
और परीक्षण ng-if
उपयोग करें!
नोट : ng-if
एक नया पृथक स्कोप बनाता है, जबकि ng-show
और ng-hide
नहीं करता है। $parent.property
उपयोग करें यदि पैरेंट स्कोप प्रॉपर्टी इसमें सीधे पहुंच योग्य नहीं है।
अपने मॉडल का त्याग करें
<div ng-controller="ExampleController">
<form name="userForm">
Name:
<input type="text" name="userName"
ng-model="user.name"
ng-model-options="{ debounce: 1000 }" />
<button ng-click="userForm.userName.$rollbackViewValue(); user.name=''">Clear</button><br />
</form>
<pre>user.name = </pre>
</div>
उपरोक्त उदाहरण हम 1000 मिलीसेकंड का एक डेबिट मूल्य निर्धारित कर रहे हैं जो 1 सेकंड है। यह एक काफी देरी है, लेकिन इनपुट को बार ng-model
बार एनग ng-model
को कई $digest
चक्रों से रोक देगा।
अपने इनपुट फ़ील्ड पर और कहीं और जहां तुरंत अपडेट की आवश्यकता नहीं है, पर बहस का उपयोग करके, आप अपने कोणीय एप्लिकेशन के प्रदर्शन को काफी हद तक बढ़ा सकते हैं। न केवल आप समय से देरी कर सकते हैं, बल्कि जब कार्रवाई शुरू हो जाती है तो आप देरी भी कर सकते हैं। यदि आप प्रत्येक कीस्ट्रोक पर अपना एनजी-मॉडल अपडेट नहीं करना चाहते हैं, तो आप ब्लर पर भी अपडेट कर सकते हैं।
हमेशा मौजूदा स्कोप के अलावा अन्य स्कोप पर पंजीकृत डेरेगिस्टर श्रोताओं को
आप हमेशा नीचे दिखाए गए अनुसार अपने वर्तमान स्कोप को अपंजीकृत करना चाहिए:
//always deregister these
$rootScope.$on(...);
$scope.$parent.$on(...);
आपको वर्तमान स्कोप पर मौजूद डेरेगिस्टर को नहीं देखना होगा क्योंकि कोणीय इसका ध्यान रखेगा:
//no need to deregister this
$scope.$on(...);
यदि आप किसी अन्य नियंत्रक $rootScope.$on
श्रोताओं $rootScope.$on
स्मृति में रहेगा। यह स्मृति रिसाव पैदा करेगा यदि नियंत्रक दायरे से बाहर हो जाता है।
नहीं
angular.module('app').controller('badExampleController', badExample);
badExample.$inject = ['$scope', '$rootScope'];
function badExample($scope, $rootScope) {
$rootScope.$on('post:created', function postCreated(event, data) {});
}
कर
angular.module('app').controller('goodExampleController', goodExample);
goodExample.$inject = ['$scope', '$rootScope'];
function goodExample($scope, $rootScope) {
var deregister = $rootScope.$on('post:created', function postCreated(event, data) {});
$scope.$on('$destroy', function destroyScope() {
deregister();
});
}