ngForとtrackBy
Angular の *ngFor
は配列をループして表示するために使用されるディレクティブです。 *ngFor
ディレクティブには trackBy
というオブジェクト追跡用のプロパティがあり、これを使用すると配列内のオブジェクトの位置変更を追跡することができます。この trackBy
を色々試してみたためメモを残しておきます。
環境
Angular CLI: 9.1.7 Node: 12.16.2 OS: win32 x64 Angular: 9.1.9 ... animations, common, compiler, compiler-cli, core, forms ... platform-browser, platform-browser-dynamic, router Ivy Workspace: Yes Package Version ----------------------------------------------------------- @angular-devkit/architect 0.901.7 @angular-devkit/build-angular 0.901.7 @angular-devkit/build-optimizer 0.901.7 @angular-devkit/build-webpack 0.901.7 @angular-devkit/core 9.1.7 @angular-devkit/schematics 9.1.7 @angular/cli 9.1.7 @ngtools/webpack 9.1.7 @schematics/angular 9.1.7 @schematics/update 0.901.7 rxjs 6.5.5 typescript 3.8.3 webpack 4.42.01
使い方
まずは trackBy
の使い方を簡単に使ってみます。
trackBy
のシグネチャーは Array
の一般的なメソッド( map
や filter
など)とは引数逆で (index: number, item: T): any
になっています。覚えづらそうなことだけ覚えていれば思い出せそうです。また TrackByFunction<T>
というインターフェースとして定義されていますので、これを型として使うのも良いでしょう。
それでは使ってみます。 TypeScript 側に trackBy
に設定するための trackByNumber
というプロパティを実装してみた一例です。今回は配列要素が Number のため、そのまま返すだけのシンプルなものになっています。
import { ChangeDetectionStrategy, Component, TrackByFunction } from '@angular/core'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush }) export class AppComponent { items: number[] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; /** * *ngForのtrackBy */ trackByNumber: TrackByFunction<number> = (index: number, item: number): any => item; }
そしてHTML テンプレート側です。 *ngFor
のダブルクオーテーションの中、セミコロンで区切りを入れてから trackBy: trackByNumber
を追加します。
<div *ngFor="let item of items; trackBy: trackByNumber"> <span>{{ item }}</span> </div>
trackBy使用有無のレンダリング差異
続いて trackBy
を使用した場合と使用していない場合で、 Angular がどのようにレンダリングを変化させるのかについてです。 Primitive と Object についてそれぞれ検証してみました。結論は以下のようにインスタンスが同一かどうかで挙動が変化します。
- 対象が Primitive の場合、要素の位置が変化しても
trackBy
有無に関わらず Component の破棄・再生成・値の変更は行われない - 対象が Object の場合、要素の位置が変化してもインスタンスが同一なら
trackBy
有無に関わらず Component の破棄・再生成・値の変更は行われない - 対象が Object の場合、インスタンスが異なると
trackBy
が存在しない場合 Component の破棄・再生成が行われる
昨今は Immutable なオブジェクトを使用することが多いですので、パフォーマンスや謎挙動防止に trackBy
を積極的に使用していくのが良いかもしれません。