Angularのテンプレート内で関数を呼び出すとパフォーマンスに影響します
Angularで関数を呼び出す行為は深刻なパフォーマンスの問題につながる可能性があります。
何が問題なのか?
Angularは本来、変更されたデータを検出し、影響の受けるDOMのみを更新するようになっている。
だが、Angularは関数呼び出しによる値の変更があるかどうかを検出できない。
つまり、テンプレート内のすべての機能は、比較のために変更検出のサイクル毎に再実行される。
例えばどういうことか?
例えばこんな風にsortedHero()といった、ソート関数をコンポーネントに定義しテンプレートから読み込んだとしたら
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'my-app',
template: `
<div *ngFor="let name of heroName">{{name}}</div>
<div style="height: 100vh; width: 100vw; background: #e3e3e3;" (click)="onClicked()"></div>
`,
})
export class AppComponent implements OnInit {
public heroNames = ['Taro','Jiro','Kiyoshi']
constructor() { }
ngOnInit() {
}
sortedHero() {
console.count("sortedHero Called");
return this.heroName.sort()
}
onClicked() {
console.count("Clicked")
}
}
(CodePenの記述は若干違いますがやろうとしていることは一緒です。)
See the Pen SortedHeroExample by kihaya-tech (@kihaya-tech) on CodePen.
例えば"sortedHero"の場合は、HeroNamesに変更がある場合がなければ影響しないように思えますが、
Angularがしっかりと変更を検出できないため比較のために再検出サイクル毎に呼び出され再実行されます。
例えばこの例の場合は、ボタンがクリックされるごとに再検出サイクルがトリガーされ呼び出されます。
とは言え?
まあ、一見ボタンをクリックし続ける奴はごく少ないのでそこまで影響がないように思えますが、Angularは変更検出をトリガーとする機能が非常にたくさんあり、関数の再実行が無視できない問題になる可能性があります。
また、いくつかの関数呼び出しだけでは、最初は影響が少ないように見えますが、コンポーネントの追加が増えると関数の実行が指数関数的に増えアプリケーションの速度を大幅に低下させる可能性があります。
じゃあ、getter関数は?
関数呼び出しを回避するために、getterメソッドを使用することを考える人もいるかもしれませんが、しかし実際には、getterメソッドも関数呼び出しであり、同じ問題が発生します。
じゃあどうすればいいの?
複雑な計算を伴うテンプレート呼び出しを回避する。計算が必要な場合は、Angularの値検出機能に頼るのではなく、手動で管理する必要がある。
プロパティーで値を管理し、親コンポーネントからの値の検知は、setter関数やngOnChangesを利用することによって対処することが適切かと思われる。
参考
この記事は、以前から疑問に思って調べていたらこの記事にあたることができ
日本語に似たようなドキュメントが少なかったため
以下の記事を参考に改編を加え投稿したものになります。
Angular: DON'T Call Functions Inside The Template Anymore