Açısal - * ngIf, şablondaki basit işlev çağrılarına karşı


14

Burada daha önce yanıtlandıysa özür dileriz, ancak özel senaryomuz için herhangi bir eşleşme bulamadım, işte gidiyor!

Geliştirme ekibimizde açısal şablonlardaki fonksiyon çağrıları hakkında bir tartışma yaptık. Şimdi genel bir kural olarak, bunları yapmamanız gerektiğine katılıyoruz. Ancak, bunun ne zaman iyi olabileceğini tartışmaya çalıştık. Size bir senaryo vereyim.

Diyelim ki, bir ngIf içine sarılmış, burada gibi birden fazla parametreyi kontrol eden bir şablon bloğumuz var:

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

Performansta aşağıdaki gibi bir şeyle karşılaştırıldığında önemli bir fark olur mu:

Şablon:

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

typescript:

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

Soruyu özetlemek gerekirse, son seçeneğin önemli bir performans maliyeti olacak mı?

2'den fazla koşulu kontrol etmemiz gereken durumlarda 2. yaklaşımı kullanmayı tercih ederiz, ancak birçok makale çevrimiçi işlev çağrılarının şablonlarda HER ZAMAN kötü olduğunu söylüyor, ancak bu durumda gerçekten bir sorun mu var?


7
Hayır. Şablonu daha okunabilir hale getirdiğinden, durum daha kolay test edilebilir ve tekrar kullanılabilir hale geldiğinden ve mümkün olduğunca okunabilir ve verimli hale getirmek için elinizde daha fazla araç (tüm TypeScript dili) vardır. Yine de "userCheck" den çok daha net bir isim seçerdim.
JB Nizet

Girişiniz için çok teşekkürler :)
Jesper

Yanıtlar:


8

Ayrıca şablonlardaki işlev çağrılarından mümkün olduğunca kaçınmaya çalıştım, ancak sorunuz hızlı bir araştırma yapmam için bana ilham verdi:

Önbellek userCheck()sonuçları olan başka bir vaka ekledim

*ngIf="isUserChecked"

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

Burada bir demo hazırladı: https://stackblitz.com/edit/angular-9qgsm9

Şaşırtıcı bir şekilde, aralarında hiçbir fark yok gibi görünüyor

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

Ve

*ngIf="userCheck()"

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

Ve

*ngIf="isUserChecked"

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

Bu, basit bir mülk kontrolü için geçerli gibi görünüyor, ancak herhangi bir asynceylem, örneğin bazı API'ler için bekleyen alıcılar söz konusu olduğunda kesinlikle bir fark olacaktır .


10

Bu oldukça fikirli bir cevap.

Bunun gibi işlevlerin kullanımı mükemmel bir şekilde kabul edilebilir. Şablonları daha net hale getirecek ve önemli bir ek yüke neden olmayacaktır. JB'nin daha önce söylediği gibi, birim testi için de çok daha iyi bir temel oluşturacaktır.

Ayrıca, şablonunuzda hangi ifadenin bulunduğunu, değişiklik algılama mekanizması tarafından bir işlev olarak değerlendirileceğini düşünüyorum, bu nedenle şablonunuzda veya bileşen mantığınızda olması önemli değildir.

Sadece fonksiyon içindeki mantığı minimumda tutun. Eğer herhangi bir performans etkisi böyle bir işlevi olabilir yaklaşık Ancak dikkatli değilseniz, şiddetle senin koymak tavsiye ChangeDetectionStrategyetmek OnPushzaten iyi uygulama olarak kabul ediliyor. Bununla, işlev her döngü olarak adlandırılmaz, yalnızca bir Inputdeğişiklik olduğunda, şablonun içinde bir olay meydana geldiğinde vb.

(vb kullanarak, çünkü artık başka bir neden bilmiyorum) .


Şahsen, yine, Gözlenebilirler modelini kullanmanın daha da güzel olduğunu düşünüyorum, daha sonra asyncboruyu kullanabilirsiniz ve sadece yeni bir değer yayıldığında şablon yeniden değerlendirilir:

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

Ardından şablonda şu şekilde kullanabilirsiniz:

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

Bileşene ngOnChangesbağlı tüm değişkenler Girişler ise ve belirli bir şablon değişkenini hesaplamak için çok fazla mantığınız varsa (gösterdiğiniz durum bu değildir), başka bir seçenek kullanmak olacaktır :

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

Şablonunuzda şöyle kullanabilirsiniz:

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

Cevabınız için teşekkürler, çok anlayışlı. Bununla birlikte, özel durumumuz için, söz konusu bileşen bir alma isteği gerçekleştirdiğinden ve bu nedenle değişiklik belirli bir girdiyle değil, alma isteğiyle ilişkili olduğu için algılama stratejisini değiştirmek bir seçenek değildir. Bununla birlikte, bu, değişikliğin girdi değişkenlerine bağlı olduğu gelecekteki bileşenlerin geliştirilmesi için çok yararlı bir bilgidir
Jesper

1
@Jesper, bileşen bir alma isteği gerçekleştirirse, zaten Observablegösterdiğim 2. seçenek için mükemmel bir aday olmasını sağlayacak bir akışınız var . Her iki durumda da, size bazı fikirler verebilirim sevindim
Poul Kruijt

6

Anapara birçok nedenden dolayı tavsiye edilmez:

UserCheck () öğesinin yeniden işlenmesi gerekip gerekmediğini belirlemek için, Angular öğesinin dönüş değerinin değişip değişmediğini kontrol etmek için userCheck () ifadesini yürütmesi gerekir.

Angular, userCheck () öğesinin dönüş değerinin değişip değişmediğini tahmin edemediğinden, değişiklik algılama her çalıştığında işlevi yürütmesi gerekir.

Bu nedenle, değişiklik algılama 300 kez çalışırsa, dönüş değeri asla değişmese bile işleve 300 kez çağrılır.

Genişletilmiş açıklama ve daha fazla sorun https://medium.com/showpad-engineering/why-you-should-never-use-function-calls-in-angular-template-expressions-e1a50f9c0496

Bileşeniniz büyükse ve birçok değişiklik etkinliğine katılırken gelen sorun, bileşen litle olacak ve sadece birkaç etkinliğe katılacaksanız sorun olmamalıdır.

Gözlenebilirler ile örnek

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;
   }
})
);

Daha sonra kodunuzda asenkron boru ile gözlemlenebilir kullanabilirsiniz.


2
Merhaba, sadece bir değişken kullanma önerisinin ciddi yanıltıcı olduğunu belirtmek istedim .. Kombine değerleri değiştiğinde değişken güncelleme olmaz
nsndvd

1
İfadenin doğrudan şablonda olması ya da bir işlev tarafından döndürülmesi, her değişiklik algılamasında değerlendirilmesi gerekir.
JB Nizet

Evet, onun gerçek üzgünüm kötü uygulamalar yapmak için düzenleyecek
anthony willis muñoz

@ anthonywillismuñoz Peki böyle bir duruma nasıl yaklaşırsınız? Sadece * ngIf'de okunması zor çoklu koşullarla yaşayın.
Jesper

1
size göre değişir orta yazı bazı seçenekler var. Ama bence gözlemlenebilir şeyler kullanıyorsunuz. Durumu azaltmak için yayını bir örnekle düzenleyecektir. Eğer koşulları nereden aldığımı bana gösterebilirsen.
anthony willis muñoz

0

JavaScript'in bir hedefle oluşturulduğunu düşünüyorum, böylece bir geliştirici performans ile ilgili bir ifade ve işlev çağrısı arasındaki farkı fark etmiyor.

C ++ 'da inlinebir işlevi işaretlemek için bir anahtar kelime vardır . Örneğin:

inline bool userCheck()
{
    return isAuthorized;
}

Bu, bir işlev çağrısını ortadan kaldırmak için yapıldı. Sonuç olarak, derleyici tüm çağrılarını userCheckişlevin gövdesi ile değiştirir . Yenilik nedeni inline? Performans artışı.

Bu nedenle, bir ifadeyle bir işlev çağrısının yürütme süresinin, muhtemelen, sadece ifadenin yürütülmesinden daha yavaş olduğunu düşünüyorum. Ancak, işlevde yalnızca bir ifadeniz varsa performansta bir fark görmeyeceğinizi düşünüyorum.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.