🐟

[Angular][ライブラリ] VirtualScrollingでパフォーマンス改善

2022/11/09に公開

VirtualScrollingとは

VirtualScrollingとはAngular Material CDKの一つで、表示されている部分のみDOMを生成するもので、パフォーマンス向上に貢献します。

通常、何千件とあるアイテムをfor文などで繰り返しレンダリングすると、見える範囲以外のすべてのDOMを一気に生成するためレスポンスが悪くなります。

そこで、このVirtualScrollingを使うことでレンダリングパフォーマンスを向上させることができます。

導入方法とポイント

Angular CDKをインストールしてあれば、そのままimportすることで使えます。

  • ScrollingModuleのインポート

    app.module.ts

    import { ScrollingModule } from '@angular/cdk/scrolling';
    @NgModule({
      declarations: [
        AppComponent,
        VirtualScrollComponent,
      ],
      imports: [
    	BrowserModule,
        AppRoutingModule,
        BrowserAnimationsModule,
        FormsModule,
        ReactiveFormsModule,
        ScrollingModule,
      ],
      providers: [],
      bootstrap: [AppComponent]
    })
    export class AppModule { }
    
  • <cdk-virtual-scroll-viewport>でスクロール対象を囲う。

  • ngForの代わりにcdkVirtualForを使う。これはngForと全く同じAPIをサポートします。

実践

virtual-scroll.component.html

<!-- itemSizeとcdkVirtualForのheightは同じにする必要があります。 -->
<cdk-virtual-scroll-viewport itemSize="20" class="virtualScroll-viewport">
  <ng-container
    *cdkVirtualFor="let item of items" class="virtualScroll-item"
  >
    <div>
      {{item}}
    </div>
  </ng-container>
</cdk-virtual-scroll-viewport>

virtual-scroll.component.css

.virtualScroll-viewport {
  height: 200px;
  width: 200px;
  border: 1px solid black;
}

.virtualScroll-item {
  height: 20px;
}
  • <cdk-virtual-scroll-viewport>itemSize属性と、cdkVirtualForheightは同じにします。

virtual-scroll.component.ts

import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-virtual-scroll',
  templateUrl: './virtual-scroll.component.html',
  styleUrls: ['./virtual-scroll.component.css']
})
export class VirtualScrollComponent implements OnInit {
  items = Array.from({length: 100000}).map((_, i) => `Item #${i}`);

  constructor() { }

  ngOnInit(): void {
  }

}

これで、見えている範囲のDOMだけを生成して読み込んでいることが分かります。

非常に多くのデータを扱う場合はこのようにVvirtualScrollやページネーションの検討が必要です。

appendOnlyモードでレンダリングされたDOMを残す

上記のように実装すると、一度読み込んだDOMがビューから外れる(スクロールして見えなくなる)と、そのDOMは削除されます。これを削除せずに生成したDOMを残すことを保証するにはappendOnlyを使います。

src\app\AngularMaterial\virtual-scroll\virtual-scroll.component.html

<!-- itemSizeとcdkVirtualForのheightは同じにする必要があります。 -->
<cdk-virtual-scroll-viewport appendOnly itemSize="20" class="example-viewport">
  <ng-container
    *cdkVirtualFor="let item of items" class="example-item"
  >
    <div>
      {{item}}
    </div>
  </ng-container>
</cdk-virtual-scroll-viewport>

※ 私の環境ではDOMは確かに残り続けるが、しばらくスクロールするとレンダリングされなくなるバグがあり、上手いこと動きませんでした。

参考

https://github.com/mgechev/angular-performance-checklist/blob/master/README.ja-JP.md#angular-パフォーマンスチェックリスト

https://material.angular.io/cdk/scrolling/overview

https://qiita.com/shira_/items/c5d323fb72e38e5e798e

https://kakkoyakakko2.hatenablog.com/entry/2018/10/23/003000

Discussion