Angular-* ngIf vsテンプレートの単純な関数呼び出し


14

これがすでにここで回答されている場合は申し訳ありませんが、私たちの特定のシナリオに一致するものが見つからなかったので、ここに行きます!

角度テンプレートの関数呼び出しに関して、開発チームで話し合いました。一般的な経験則として、私たちはあなたがこれらを行うべきではないことに同意します。しかし、それがいつ大丈夫かについて話し合うようにしました。シナリオを紹介しましょう。

ngIfでラップされたテンプレートブロックがあり、次のように複数のパラメーターをチェックするとします。

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

次のようなものと比較してパフォーマンスに大きな違いがありますか?

テンプレート:

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

Typescript:

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

では、質問を要約すると、最後のオプションには大きなパフォーマンスコストがありますか?

2つ以上の条件をチェックする必要がある状況では、2番目のアプローチを使用したいと思いますが、オンラインの多くの記事では、関数呼び出しは常にテンプレートで悪いと述べていますが、この場合本当に問題ですか?


7
いいえ、そうではありません。また、テンプレートも読みやすくなり、条件を簡単にテストおよび再利用できるようになり、可能な限り読みやすく効率的にするためのツール(TypeScript言語全体)が増えます。ただし、「userCheck」よりも明確な名前を選択します。
JB Nizet

入力ありがとうございます:)
Jesper

回答:


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何らかのAPI(たとえば、いくつかのAPIを待機しているゲッター)に関しては、明らかに違いがあります。


10

これはかなり独断的な答えです。

このような関数の使用は完全に許容されます。これにより、テンプレートがより明確になり、大きなオーバーヘッドは発生しません。JBが前に言ったように、それは同様にユニットテストのためのはるかに良いベースを設定します。

また、テンプレート内にある式はすべて、変更検出メカニズムによって関数として評価されるので、テンプレートまたはコンポーネントロジック内にあるかどうかは関係ありません。

関数内のロジックを最小限に抑えるだけです。あなたは、このような機能を持っている可能性のあるパフォーマンスへの影響についてしかし、警戒している場合、私は強くあなたの置くことをアドバイスChangeDetectionStrategyOnPushとにかくベストプラクティスと考えています、。これにより、関数がサイクルごとに呼び出されるのではなく、Input変更、テンプレート内で何らかのイベントが発生した場合などにのみ呼び出されます。

(他の理由がわからないため、etcを使用)


個人的にも、Observablesパターンを使用する方がいいと思います。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>

お返事ありがとうございます。ただし、特定のケースでは、問題のコンポーネントがgetリクエストを実行するため、検出戦略の変更はオプションではありません。したがって、変更は特定の入力ではなく、getリクエストに関連しています。それでも、これは変更が入力変数に依存する将来のコンポーネントの開発に非常に役立つ情報です
Jesper

1
@Jesperは、コンポーネントがgetリクエストを実行する場合、すでにObservableストリームを持っているため、私が示した2番目のオプションの完全な候補になります。いずれにせよ、いくつかの洞察を提供できてうれしい
ポールクルート

6

多くの理由でプリンシパルは推奨されません:

userCheck()を再レンダリングする必要があるかどうかを判断するには、AngularがuserCheck()式を実行して、戻り値が変更されたかどうかを確認する必要があります。

AngularはuserCheck()の戻り値が変更されたかどうかを予測できないため、変更検出が実行されるたびに関数を実行する必要があります。

したがって、変更検出が300回実行される場合、戻り値が変更されない場合でも、関数は300回呼び出されます。

詳細な説明とその他の問題https://medium.com/showpad-engineering/why-you-should-never-use-function-calls-in-angular-template-expressions-e1a50f9c0496

urコンポーネントが大きく、多くの変更イベントに参加する場合に発生する問題、コンポーネントがわずかで、いくつかのイベントに参加するだけの場合は問題になりません。

オブザーバブルの例

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

次に、コードで非同期パイプを使用して監視可能にすることができます。


2
こんにちは、変数を使用することの提案が非常に誤解を招くことがわかったと指摘したいだけです。結合された値のいずれかが変更されると、変数は値を更新しません
nsndvd

1
そして、式が直接テンプレートにあるか、関数によって返されるかに関係なく、変更が検出されるたびに評価する必要があります。
JB Nizet

はい、本当の申し訳ありませんが、悪習をしないように編集します
anthony willismuñozJan

@anthonywillismuñozでは、このような状況にどのように取り組みますか?* ngIfの複数の読みにくい条件に対処するだけです。
Jesper

1
状況によって異なりますが、中程度の投稿にいくつかのオプションがあります。しかし、私はあなたがオブザーバブルを使用していると思います。条件を減らすために例を使用して投稿を編集します。あなたが条件を取得する場所から私を示すことができれば。
アンソニーウィリスムニョス

0

JavaScriptは、開発者がパフォーマンスに関する式と関数呼び出しの違いに気付かないようにすることを目標に作成されたと思います。

C ++にはキーワードがあります inline、関数をマークするがあります。例えば:

inline bool userCheck()
{
    return isAuthorized;
}

これは、関数呼び出しを排除するために行われました。その結果、コンパイラはのすべての呼び出しを置き換えますuserCheck関数の本体にます。革新の理由はinline?パフォーマンスの向上。

したがって、1つの式を使用した関数呼び出しの実行時間は、おそらく式のみの実行よりも遅いと思います。ただし、関数の式が1つしかない場合でも、パフォーマンスの違いに気付かないと思います。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.