कोणीय - * ngIf बनाम सरल फ़ंक्शन टेम्पलेट में


14

क्षमा करें यदि यह पहले से ही यहाँ उत्तर दिया गया है, लेकिन मुझे हमारे विशिष्ट परिदृश्य के लिए कोई भी मैच नहीं मिला, इसलिए यहाँ जाता है!

हमें कोणीय टेम्प्लेट में फ़ंक्शन कॉल के बारे में हमारी विकास टीम में चर्चा हुई। अब अंगूठे के एक सामान्य नियम के रूप में, हम सहमत हैं कि आपको ये नहीं करना चाहिए। हालाँकि, हमने चर्चा करने की कोशिश की है कि यह कब ठीक हो सकता है। मैं आपको एक परिदृश्य देता हूं।

मान लें कि हमारे पास एक टेम्पलेट ब्लॉक है जो एनजीआईएफ में लिपटे हुए है, जो कई मापदंडों की जांच करता है, जैसे कि:

<ng-template *ngIf="user && user.name && isAuthorized">
 ...
</ng-template>

क्या कुछ इस तरह की तुलना में प्रदर्शन में महत्वपूर्ण अंतर होगा:

टेम्पलेट:

<ng-template *ngIf="userCheck()">
 ...
</ng-template>

टाइपप्रति:

userCheck(): boolean {
  return this.user && this.user.name && this.isAuthorized;
}

तो प्रश्न को संक्षेप में बताने के लिए, क्या अंतिम विकल्प के पास कोई महत्वपूर्ण प्रदर्शन लागत होगी?

हम 2 दृष्टिकोण का उपयोग करना पसंद करेंगे, ऐसी स्थितियों में जहां हमें 2 से अधिक स्थितियों की जांच करने की आवश्यकता है, लेकिन कई लेख ऑनलाइन कहते हैं कि फ़ंक्शन कॉल टेम्पलेट्स में खराब हैं, लेकिन क्या इस मामले में वास्तव में समस्या है?


7
नहीं, यह नहीं होगा। और यह क्लीनर भी है, क्योंकि यह टेम्पलेट को अधिक पठनीय बनाता है, स्थिति अधिक आसानी से परीक्षण योग्य और पुन: प्रयोज्य है, और आपके पास अपने निपटान (संपूर्ण टाइपस्क्रिप्ट भाषा) में अधिक से अधिक उपकरण हैं ताकि इसे यथासंभव पठनीय और कुशल बनाया जा सके। मैं "userCheck" की तुलना में बहुत स्पष्ट नाम चुनूंगा, हालाँकि।
जेबी निजेट

आपके इनपुट के लिए बहुत बहुत धन्यवाद :)
जेस्पर

जवाबों:


8

मैंने जितना हो सके टेम्प्लेट्स में फ़ंक्शंस कॉल से बचने की कोशिश की, लेकिन आपके प्रश्न ने मुझे त्वरित शोध करने के लिए प्रेरित किया:

मैंने कैशिंग userCheck()परिणामों के साथ एक और मामला जोड़ा

*ngIf="isUserChecked"

...
// .ts
isUserChecked = this.userCheck()

यहाँ एक डेमो तैयार किया: https://stackblitz.com/edit/angular-9qgsm9

हैरानी की बात यह है कि ऐसा लगता है कि इसमें कोई अंतर नहीं है

*ngIf="user && user.name && isAuthorized"

तथा

*ngIf="userCheck()"

...
// .ts
userCheck(): boolean {
  return this.user && this.user.name && this.isAuthorized;
}

तथा

*ngIf="isUserChecked"

...
// .ts
isUserChecked = this.userCheck()

ऐसा लगता है कि यह एक साधारण संपत्ति की जाँच के लिए वैध है, लेकिन निश्चित रूप से एक अंतर होगा यदि यह किसी भी asyncकार्य के लिए आता है , तो ऐसे गेटर्स जो उदाहरण के लिए कुछ एपीआई की प्रतीक्षा कर रहे हैं।


10

यह एक सुंदर राय है।

इस तरह के कार्यों का उपयोग, पूरी तरह से स्वीकार्य है। यह टेम्प्लेट्स को अधिक स्पष्ट कर देगा, और यह किसी भी महत्वपूर्ण ओवरहेड का कारण नहीं बनता है। जेबी ने पहले कहा, यह इकाई परीक्षण के लिए और भी बेहतर आधार तय करेगा।

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

केवल फ़ंक्शन के अंदर तर्क को न्यूनतम रखें। आप फिर भी सावधान किसी भी प्रदर्शन प्रभाव इस तरह के एक समारोह हो सकता है के बारे में कर रहे हैं, मैं दृढ़ता से आप अपने डाल करने के लिए सलाह देने ChangeDetectionStrategyके लिए OnPushहै, जो सबसे अच्छा अभ्यास वैसे भी माना जाता है। इसके साथ, फ़ंक्शन को हर चक्र नहीं कहा जाएगा, केवल तब जब कोई Inputपरिवर्तन, कुछ ईवेंट टेम्पलेट के अंदर होता है, आदि।

(आदि का उपयोग करते हुए, क्योंकि मुझे अब और कारण पता नहीं है)


व्यक्तिगत रूप से, फिर से, मुझे लगता है कि ऑब्जर्वबल्स पैटर्न का उपयोग करना भी अच्छा है, आप तब asyncपाइप का उपयोग कर सकते हैं , और केवल तब जब कोई नया मूल्य उत्सर्जित होता है, तो टेम्पलेट का पुनर्मूल्यांकन किया जाता है:

userIsAuthorized$ = combineLatest([
  this.user$,
  this.isAuthorized$
]).pipe(
  map(([ user, authorized ]) => !!user && !!user.name && authorized),
  shareReplay({ refCount: true, bufferSize: 1 })
);

फिर आप इस तरह से टेम्पलेट में उपयोग कर सकते हैं:

<ng-template *ngIf="userIsAuthorized$ | async">
 ...
</ng-template>

फिर भी एक अन्य विकल्प का उपयोग करना होगा ngOnChanges, यदि घटक के सभी आश्रित चर इनपुट हैं, और आपके पास एक निश्चित टेम्पलेट चर की गणना करने के लिए बहुत सारे तर्क हैं (जो आपके द्वारा दिखाए गए मामले में नहीं है):

export class UserComponent implements ngOnChanges {
  userIsAuthorized: boolean = false;

  @Input()
  user?: any;

  @Input()
  isAuthorized?: boolean;

  ngOnChanges(changes: SimpleChanges): void {
    if (changes.user || changes.isAuthorized) {
      this.userIsAuthorized = this.userCheck();
    }
  }

  userCheck(): boolean {
    return this.user && this.user.name && this.isAuthorized || false;
  }
}

जिसे आप इस तरह से अपने टेम्पलेट में उपयोग कर सकते हैं:

<ng-template *ngIf="userIsAuthorized">
 ...
</ng-template>

आपके उत्तर के लिए धन्यवाद, बहुत ही व्यावहारिक। हालांकि हमारे विशिष्ट मामले के लिए, पता लगाने की रणनीति बदलना एक विकल्प नहीं है, क्योंकि प्रश्न में घटक एक अनुरोध प्राप्त करता है, और इसलिए परिवर्तन एक विशिष्ट इनपुट से संबंधित नहीं है, बल्कि अनुरोध प्राप्त करें। बहरहाल, यह भविष्य के घटकों के विकास के लिए बहुत उपयोगी जानकारी है जहां परिवर्तन इनपुट चर पर निर्भर है
जेस्पर

1
@ जैस्पर यदि घटक एक अनुरोध प्राप्त करता है, तो आपके पास पहले से ही एक Observableस्ट्रीम है, जो उसे दिखाए गए 2 विकल्प के लिए एक आदर्श उम्मीदवार बना देगा। किसी भी तरह से, खुशी है कि मैं आपको कुछ अंतर्दृष्टि दे सकता हूं
पौल क्रुजित

6

मूल कारणों के लिए अनुशंसित नहीं है:

यह निर्धारित करने के लिए कि क्या userCheck () को फिर से प्रस्तुत करने की आवश्यकता है, कोणीय को उपयोगकर्ता जाँच () अभिव्यक्ति को निष्पादित करने की आवश्यकता है कि क्या इसकी वापसी का मान बदल गया है।

क्योंकि कोणीय यह अनुमान नहीं लगा सकता है कि क्या userCheck () का रिटर्न मान बदल गया है, इसे हर बार परिवर्तन का पता लगाने के लिए फ़ंक्शन को निष्पादित करना होगा।

इसलिए यदि परिवर्तन का पता 300 बार चलता है, तो फ़ंक्शन को 300 बार कहा जाता है, भले ही उसका रिटर्न मान कभी न बदले।

विस्तारित विवरण और अधिक समस्याओं https://medium.com/showpad-engineering/why-you-should-never-use-function-calls-in-angular-template-expressions-e1a50f9c0496

समस्या जब यूर घटक बड़ा है और कई परिवर्तन घटनाओं में भाग लेते हैं, यदि आप घटक जलाएंगे और बस कुछ घटनाओं में भाग लेंगे तो समस्या नहीं होनी चाहिए।

वेधशालाओं के साथ उदाहरण

user$;
isAuth$
userCheck$;

userCheck$ = user$.pipe(
switchMap((user) => {
    return forkJoin([of(user), isAuth$]);
 }
)
.map(([user, isAuthenticated])=>{
   if(user && user.name && isAuthenticated){
     return true;
   } else {
     return false;
   }
})
);

तब आप इसे async पाइप के साथ देखने योग्य कोड में उपयोग कर सकते हैं।


2
नमस्ते, बस यह इंगित करना चाहता था कि मुझे एक चर का उपयोग करने का सुझाव गंभीरता से भ्रामक है .. चर अद्यतन नहीं होगा जब कोई भी संयुक्त मूल्य बदलता है
nsndvd

1
और क्या अभिव्यक्ति सीधे टेम्पलेट में है, या किसी फ़ंक्शन द्वारा लौटाया गया है, इसे प्रत्येक परिवर्तन का पता लगाने पर मूल्यांकन करना होगा।
जेबी निज़ेट

हां, इसका सही खेद बुरी प्रथाओं को न करने के लिए संपादित होगा
एंथनी विलिस मुनोज

@ anthonywillismuñoz तो आप इस तरह की स्थिति से कैसे संपर्क करेंगे? बस * ngIf में कई, कठिन-से-पढ़ने की शर्तों के साथ रहते हैं?
जेस्पर

1
कि आप स्थिति में निर्भर करता है आप मध्यम पोस्ट में कुछ विकल्प हैं। लेकिन मुझे लगता है कि आप वेधशालाओं का उपयोग कर रहे हैं। स्थिति को कम करने के लिए एक उदाहरण के साथ पोस्ट को संपादित करेंगे। यदि आप मुझे दिखा सकते हैं कि आपको कहां से शर्तें मिलेंगी।
एन्थोनी विलिस म्यूएनोज

0

मुझे लगता है कि जावास्क्रिप्ट को एक लक्ष्य के साथ बनाया गया था ताकि एक डेवलपर को प्रदर्शन के संबंध में एक एक्सप्रेशन और फ़ंक्शन कॉल के बीच अंतर न दिखाई दे।

C ++ inlineमें फंक्शन को मार्क करने के लिए एक कीवर्ड है। उदाहरण के लिए:

inline bool userCheck()
{
    return isAuthorized;
}

यह एक फ़ंक्शन कॉल को समाप्त करने के लिए किया गया था। नतीजतन, संकलक userCheckफ़ंक्शन के शरीर के साथ सभी कॉलों की जगह लेता है । नया करने का कारण inline? एक प्रदर्शन को बढ़ावा देने के।

इसलिए, मुझे लगता है कि एक फ़ंक्शन का निष्पादन समय एक अभिव्यक्ति के साथ कॉल करता है, शायद, केवल एक्सप्रेशन के निष्पादन की तुलना में धीमा है। लेकिन, मुझे यह भी लगता है कि यदि आपके पास फ़ंक्शन में केवल एक अभिव्यक्ति है, तो आपको प्रदर्शन में अंतर नहीं दिखाई देगा।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.