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