@for
ループで指定されたアイデンティティトラック式が、_すべての_アイテムに対応するDOMの再作成を引き起こしました。これは、イミュータブルなデータ構造を扱う際に発生する一般的な操作で、非常に高価です。例:
@Component({ template: ` <button (click)="toggleAllDone()">All done!</button> <ul> @for (todo of todos; track todo) { <li>{{todo.task}}</li> } </ul> `,})export class App { todos = [ { id: 0, task: 'understand trackBy', done: false }, { id: 1, task: 'use proper tracking expression', done: false }, ]; toggleAllDone() { this.todos = this.todos.map(todo => ({ ...todo, done: true })); }}
この例では、アイテムの "done" ステータスを切り替えた後、すべてのビュー(DOMノード、Angularディレクティブ、コンポーネント、クエリなど)を含むリスト全体が再作成されます(!)。ここでは、done
プロパティへの比較的安価なバインディング更新で十分です。
DOMツリーを再作成すると、高いパフォーマンスペナルティに加えて、DOM要素のステート(例:フォーカス、テキストの選択、iframeでロードされたサイトなど)が失われます。
エラーの修正
アイテムのオブジェクトアイデンティティに関係なく、コレクション内のアイテムをユニークに識別するようにトラック式を変更します。説明した例では、正しいトラック式はユニークな id
プロパティ(item.id
)を使用します。
@Component({ template: ` <button (click)="toggleAllDone()">All done!</button> <ul> @for (todo of todos; track todo.id) { <li>{{todo.task}}</li> } </ul> `,})export class App { todos = [ { id: 0, task: 'understand trackBy', done: false }, { id: 1, task: 'use proper tracking expression', done: false }, ]; toggleAllDone() { this.todos = this.todos.map(todo => ({ ...todo, done: true })); }}