使用 Angular CDK 將大型清單虛擬化

實作虛擬捲動,讓大型清單的回應速度更快。

Stephen Fluin
Stephen Fluin

無論是在喜愛的社群媒體網站上瀏覽無限捲動的動態消息,還是瀏覽企業資訊主頁,捲動清單都是現今最常見的 UI 模式之一。如果捲動清單過長 (數百、數千或數十萬個項目),應用程式效能可能會受到影響。

大型清單載入速度可能較慢,因為應用程式必須預先載入及算繪所有資料。此外,清單中的每個項目都可能包含豐富的資料、媒體和功能,因此可能導致轉譯和瀏覽速度緩慢。

使用者載入或捲動網頁時可能會遇到問題,導致挫敗感,進而放棄瀏覽網頁。

使用元件開發套件在 Angular 中進行虛擬捲動

虛擬捲動是解決這些規模問題的主要技術。 虛擬捲動功能會提供適當大小的捲軸,營造出大型清單的印象,並讓您瀏覽清單,不必讓應用程式將整個清單保留在記憶體中,或在網頁上算繪清單。

在 Angular 中,虛擬捲動功能是由元件開發套件 (CDK) 提供。只要修改清單的疊代方式,並提供幾個額外的設定參數,CDK 的虛擬捲動功能就會自動管理清單的虛擬算繪作業,進而提升網頁效能和回應速度。

系統不會一次轉譯整個清單,只會轉譯螢幕上顯示的項目子集 (加上小型緩衝區)。使用者瀏覽時,系統會計算並轉譯新的項目子集,並視需要重複使用現有的 DOM。

本文的其餘部分將逐步說明如何設定基本虛擬捲動。如需完整的有效範例,請參閱這個範例應用程式:

設定虛擬捲動

首先,請確認你已使用偏好的套件管理工具安裝 @angular/cdk。如要使用 npm 安裝,請在終端機中執行下列指令:

npm install --save @angular/cdk

在應用程式中新增 ScrollingModule

安裝 CDK 後,請從 @angular/cdk/scrolling 套件匯入 ScrollingModule,處理虛擬捲動。然後將其新增至模組的匯入陣列:

import {ScrollingModule} from '@angular/cdk/scrolling';

...
imports: [
  ScrollingModule
...
]
...

建立可視區域

如要瞭解套件的運作方式,請嘗試建立元件,其中包含 0 到 99,999 的數字清單:

@Component({
  template: `<div *ngFor="let item of list">{{item}}</div>`
})
export class ScrollComponent {
  list = Array.from({length: 100000}).map((_, i) => i);
}

瀏覽器算繪應用程式時,必須算繪 10 萬個個別 <div> 元素。這對基本文字節點來說或許沒問題,但重複範本中的任何複雜性都無法妥善擴充,而且任何事件監聽器都會大幅增加。

如要新增虛擬捲動功能並避免這些問題,請在 <cdk-virtual-scroll-viewport> 元素中包裝清單,藉此建立檢視區塊:

@Component({
  template: `<cdk-virtual-scroll-viewport>
    <div *ngFor="let item of list">{{item}}</div>
    </cdk-virtual-scroll-viewport>`
})
export class ScrollComponent {
  list = Array.from({length: 100000}).map((_, i) => i);
}

由於 ScrollingModule 會動態算繪清單的子集,因此您必須使用標準 CSS 指定檢視區塊的高度。您也需要指定 itemSize,向檢視區塊提供內容提示。模組會使用這項資訊,判斷要在 DOM 中保留多少項目,以及如何算繪適當大小的捲軸。

@Component({
  template: `<cdk-virtual-scroll-viewport itemSize="18" style="height:80vh">
    <div *ngFor="let item of list">{{item}}</div>
    </cdk-virtual-scroll-viewport>`
})
export class ScrollComponent {
  list = Array.from({length: 100000}).map((_, i) => i);
}

最後,將 *ngFor 轉換為 *cdkVirtualFor

@Component({
  template: `<cdk-virtual-scroll-viewport itemSize="18" style="height:80vh">
    <div *cdkVirtualFor="let item of list">{{item}}</div>
    </cdk-virtual-scroll-viewport>`
})
export class ScrollComponent {
  list = Array.from({length: 100000}).map((_, i) => i);
}

可視區域會動態識別並逐一檢查清單中正確的子集,而非逐一檢查整個清單。現在使用者載入網頁時,CDK 應會算繪符合畫面的清單子集 (加上少許緩衝區),且檢視區塊中的任何捲動事件都會載入並算繪適當的清單子集:

CDK 會在使用者捲動清單時,算繪清單的子集。

進一步瞭解

CDK 的虛擬捲動功能遠遠超出這個基本範例。在範例應用程式中,整個清單都位於記憶體中,但對於更複雜的應用程式,清單可以視需要擷取。如要進一步瞭解 ScrollingModule 的其他功能和 cdkVirtualOf 指令,請參閱 CDK 說明文件中的 Scrolling