Angular - * ngIf vs panggilan fungsi sederhana dalam template

14

Maaf jika ini sudah dijawab di sini, tetapi saya tidak dapat menemukan kecocokan untuk skenario khusus kami, jadi begini!

Kami telah berdiskusi di tim pengembangan kami, tentang pemanggilan fungsi dalam templat sudut. Sekarang sebagai aturan umum, kami setuju bahwa Anda tidak boleh melakukan ini. Namun, kami sudah mencoba mendiskusikan kapan itu boleh saja. Biarkan saya memberi Anda sebuah skenario.

Katakanlah kita memiliki blok template yang dibungkus dengan ngIf, yang memeriksa beberapa parameter, seperti di sini:

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

Apakah akan ada perbedaan kinerja yang signifikan dibandingkan dengan yang seperti ini:

Templat:

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

Naskah:

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

Jadi, untuk meringkas pertanyaan, apakah opsi terakhir memiliki biaya kinerja yang signifikan?

Kami lebih suka menggunakan pendekatan ke-2, dalam situasi di mana kami perlu memeriksa lebih dari 2 kondisi, tetapi banyak artikel online mengatakan panggilan fungsi SELALU buruk dalam templat, tetapi apakah ini benar-benar masalah dalam kasus ini?

Jesper
sumber
7
Tidak, tidak akan. Dan itu lebih bersih juga, karena membuat templat lebih mudah dibaca, kondisinya lebih mudah diuji dan dapat digunakan kembali, dan Anda memiliki lebih banyak alat yang Anda inginkan (seluruh bahasa TypeScript) untuk menjadikannya dapat dibaca dan seefisien mungkin. Saya akan memilih nama yang jauh lebih jelas daripada "userCheck".
JB Nizet
Terima kasih banyak atas masukan Anda :)
Jesper

Jawaban:

8

Saya juga mencoba untuk menghindari panggilan fungsi dalam template sebanyak mungkin, tetapi pertanyaan Anda menginspirasi saya untuk melakukan riset cepat:

Saya menambahkan kasus lain dengan userCheck()hasil caching

*ngIf="isUserChecked"

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

Mempersiapkan demo di sini: https://stackblitz.com/edit/angular-9qgsm9

Anehnya sepertinya tidak ada perbedaan di antara keduanya

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

Dan

*ngIf="userCheck()"

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

Dan

*ngIf="isUserChecked"

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

Sepertinya ini valid untuk pemeriksaan properti sederhana, tetapi pasti akan ada perbedaan jika menyangkut asynctindakan apa pun , getter yang sedang menunggu api misalnya.

qiAlex
sumber
10

Ini adalah jawaban yang cukup keras.

Penggunaan fungsi seperti ini, sangat bisa diterima. Ini akan membuat templat lebih jelas, dan tidak menyebabkan overhead yang signifikan. Seperti JB katakan sebelumnya, itu akan menetapkan basis yang jauh lebih baik untuk pengujian unit juga.

Saya juga berpikir bahwa ekspresi apa pun yang Anda miliki di template Anda, akan dievaluasi sebagai fungsi oleh mekanisme deteksi perubahan, jadi tidak masalah jika Anda memilikinya di template Anda atau di komponen logika Anda.

Biarkan logika di dalam fungsi tetap minimum. Jika Anda namun waspada tentang dampak kinerja fungsi tersebut mungkin memiliki, saya sangat menyarankan Anda untuk menempatkan Anda ChangeDetectionStrategyke OnPush, yang dianggap praktek terbaik lagian. Dengan ini, fungsi tidak akan dipanggil setiap siklus, hanya ketika ada Inputperubahan, beberapa peristiwa terjadi di dalam template, dll.

(menggunakan dll, karena saya tidak tahu alasan lain lagi) .


Secara pribadi, sekali lagi, saya pikir itu lebih baik untuk menggunakan pola yang dapat diobservasi, Anda kemudian dapat menggunakan asyncpipa, dan hanya ketika nilai baru dipancarkan, template akan dievaluasi kembali:

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

Anda kemudian dapat menggunakan dalam template seperti ini:

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

Namun pilihan lain adalah menggunakan ngOnChanges, jika semua variabel dependen ke komponen adalah Input, dan Anda memiliki banyak logika yang terjadi untuk menghitung variabel templat tertentu (yang bukan kasus yang Anda tunjukkan):

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

Yang dapat Anda gunakan dalam template Anda seperti ini:

<ng-template *ngIf="userIsAuthorized">
 ...
</ng-template>
Poul Kruijt
sumber
Terima kasih atas balasan Anda, sangat mendalam. Namun untuk kasus khusus kami, mengubah strategi deteksi bukanlah suatu opsi, karena komponen yang dimaksud melakukan permintaan dapatkan, dan oleh karena itu perubahan tidak terkait dengan input tertentu, melainkan permintaan dapatkan. Meskipun demikian, ini adalah informasi yang sangat berguna untuk pengembangan komponen masa depan di mana perubahan tergantung pada variabel input
Jesper
1
@Jesper jika komponen melakukan permintaan dapatkan, maka Anda sudah memiliki Observablealiran, yang akan membuatnya menjadi kandidat yang sempurna untuk opsi ke-2 yang saya tunjukkan. Either way, senang saya bisa memberi Anda beberapa wawasan
Poul Kruijt
6

Tidak direkomendasikan karena berbagai alasan kepala sekolah:

Untuk menentukan apakah userCheck () perlu dirender ulang, Angular perlu mengeksekusi ekspresi userCheck () untuk memeriksa apakah nilai pengembaliannya telah berubah.

Karena Angular tidak dapat memprediksi apakah nilai pengembalian userCheck () telah berubah, ia perlu menjalankan fungsi setiap kali deteksi perubahan berjalan.

Jadi jika deteksi perubahan berjalan 300 kali, fungsinya disebut 300 kali, bahkan jika nilai baliknya tidak pernah berubah.

Penjelasan lebih lanjut dan lebih banyak masalah https://medium.com/showpad-engineering/why-you-should-never-use-function-calls-in-angular-template-expressions-e1a50f9c0496

Masalahnya muncul ketika jika komponen Anda besar dan menghadiri banyak acara perubahan, jika komponen Anda akan menyala dan hanya menghadiri beberapa acara seharusnya tidak menjadi masalah.

Contoh dengan diamati

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

Kemudian Anda dapat menggunakannya dengan pipa async yang dapat diamati dalam kode Anda.

anthony willis muñoz
sumber
2
Hai, hanya ingin menunjukkan bahwa saya menemukan saran menggunakan variabel benar-benar menyesatkan .. Variabel tidak akan memperbarui adalah nilai ketika salah satu nilai gabungan berubah
nsndvd
1
Dan apakah ekspresi langsung di templat, atau dikembalikan oleh suatu fungsi, itu harus dievaluasi pada setiap deteksi perubahan.
JB Nizet
Ya itu benar maafnya akan diedit karena tidak melakukan praktik buruk
anthony willis muñoz
@ anthonywillismuñoz Jadi, bagaimana Anda mendekati situasi seperti ini? Hanya tinggal dengan beberapa kondisi yang sulit dibaca di * ngIf?
Jesper
1
itu tergantung pada situasi Anda, Anda memiliki beberapa opsi dalam posting media. Tapi saya pikir Anda menggunakan yang bisa diamati. Akan mengedit tulisan dengan contoh untuk mengurangi kondisi. jika Anda bisa menunjukkan kepada saya dari mana Anda mendapatkan kondisinya.
anthony willis muñoz
0

Saya pikir JavaScript dibuat dengan tujuan agar pengembang tidak melihat perbedaan antara ekspresi dan panggilan fungsi terkait kinerja.

Di C ++ ada kata kunci inlineuntuk menandai suatu fungsi. Sebagai contoh:

inline bool userCheck()
{
    return isAuthorized;
}

Ini dilakukan untuk menghilangkan panggilan fungsi. Akibatnya, kompiler menggantikan semua panggilan userCheckdengan isi fungsi. Alasan untuk berinovasi inline? Peningkatan kinerja.

Oleh karena itu, saya berpikir bahwa waktu eksekusi pemanggilan fungsi dengan satu ekspresi, mungkin, lebih lambat daripada eksekusi dari ekspresi saja. Tapi, saya juga berpikir Anda tidak akan melihat perbedaan dalam kinerja jika Anda hanya memiliki satu ekspresi dalam fungsinya.

alexander.sivak
sumber