Beschleunigen Sie das Rendern von MatTables in wenigen Schritten

Es kommt häufig vor, dass der größte Teil der Anwendung aus verschiedenen Listen und Tabellen besteht. Um das Rad nicht jedes Mal neu zu erfinden, habe ich, wie viele, oft Angular Material-Tabellen verwendet .

Später wuchsen diese Tabellen, sowohl Textdaten als auch verschachtelte Elemente wurden in Zellen platziert. All dies wächst und wird zu einer guten Belastung für den Computer des Benutzers. Und das gefällt natürlich niemandem.

In meinem letzten Heimprojekt gab es eine Tabelle, deren Zellen größtenteils mit verschiedenen Feldern gefüllt waren (man könnte sogar sagen, dass es sich um eine große Form handelte).

Das Rendern dauerte ungefähr 8 Sekunden (40 x 40 Tabelle).

Wie können Sie MatTable für große Listen optimieren?

Bild

Testfall


Um anderen bei der Bewältigung dieses Problems zu helfen, habe ich eine kleine Testanwendung geschrieben. Dieselbe MatTable-Tabelle mit fünf Spalten (die erste ist die ID des Elements, der Rest sind reguläre MatFormField-Textfelder).



Es scheint, dass es sich um eine einfache Tabelle handelt, aber selbst das Rendern von 100 dauert nicht länger als 2 Sekunden (Oh, dieses Material und seine "Top" -Leistung).



Das Rendern selbst dauerte in diesem Fall nur (860 ms), die Seite blieb jedoch 2,2 Sekunden lang stehen und der Benutzer war verärgert.

Versuchen wir, eine Tabelle mit 300 Zeilen zu rendern. Und die Trommel rollen, wir warten ein bisschen und wir sehen, dass fast 10 Sekunden für das Scripten und Rendern insgesamt aufgewendet wurden. Das heißt, wenn der Benutzer eine solche Tabelle in 300 Zeilen anzeigen möchte, friert seine Seite 10 Sekunden lang ein. Das ist sehr beängstigend und erstaunlich (eigentlich nicht).



Tatsächlich habe ich immer noch versucht, die Zeit zu messen, die zum Zeichnen von 1000 Elementen benötigt wird, aber mein erbärmlicher i7 konnte es nicht aushalten und die Seite fiel ständig ab.
Wir werden später versuchen, dies mit der bereits angewendeten Lösung zu tun.

Lösung


Daher kann jeder mit browserbasierten Dienstprogrammen Messungen durchführen, aber es gibt keine Lösung für dieses Problem.

Ich wurde zu dieser Entscheidung geführt, indem ich über das Wesentliche des Problems nachdachte.
Ein korrektes Verständnis des Problems ist bereits mindestens die Hälfte seiner Lösung.
Ich denke, jeder versteht, dass dies geschieht, weil die Tabellendaten zuerst von Skripten verarbeitet und dann in einem Stück ausgespuckt werden . Genau deshalb sehen wir wiederum eine charakteristische Federung.

Problem gefunden. Jetzt bleibt herauszufinden, wie man es löst. Die allererste Lösung, die mir in den Sinn kommt, ist die einfachste. Wenn das Problem darin besteht, alle Daten gleichzeitig zu verarbeiten, müssen Sie die Tabelle veranlassen, die Daten in Teilen zu verarbeiten.

Aber wie geht das?

Lassen Sie uns überlegen, was wir dafür sofort haben. Das erste, was mir in den Sinn kommt, ist die Track By-Funktion. Beim Ändern der Datenquelle wird nicht die gesamte Tabelle neu gerendert, sondern nur die Änderungen.

Fügen wir diese Funktion unserer Tabelle hinzu:

<mat-table [trackBy]="trackByFn" [dataSource]="commonDataSource">

Nun, aber wir haben keine solche Bedingung, dass sich die Tabellendaten irgendwie ändern, und darum geht es in der Konversation jetzt nicht. Was aber, wenn wir Pipe schreiben, das beim Initialisieren der Datenquelle die Daten bricht und sie stapelweise an die Tabelle weitergibt? Die trackBy-Funktion hilft wiederum dabei, einen vollständigen Renderer zu vermeiden.

@Pipe({
  name: 'splitSchedule'
})
export class SplitPipe implements PipeTransform {
  public transform(value: any, takeBy: number = 4, throttleTime: number = 40): Observable<Array<any>> {
    return Array.isArray(value)
      ? this.getSplittedThread(value, takeBy, throttleTime)
      : of(value);
  }

  private getSplittedThread(data: Array<any>, takeBy: number, throttleTime: number): Observable<Array<any>> {
    const repeatNumber = Math.ceil(data.length / takeBy);
    return timer(0, throttleTime).pipe(
      map((current) => data.slice(0, takeBy * ++current)),
      take(repeatNumber)
    );
  }
}

Hier ist ein so kleiner Code, mit dem Ihre Hardware so große Tabellen rendern kann.

Wenden Sie diese Pipe auf unsere Datenquelle an.


<mat-table [trackBy]="trackByFn"
           [dataSource]="commonDataSource | splitSchedule | async">

Versuchen wir nun, Messungen vorzunehmen. Rendern Sie 100, 300 und 1000 Elemente.







Und was sehen wir? Was wirklich Erfolg ist, ist nicht das, was wir erwartet haben:

  • 300 Elemente werden 1 Sekunde schneller gerendert
  • 1000 in 11 Sekunden gerendert und der Tab ist nicht gestorben
  • und 100 Elemente werden im Allgemeinen 150 ms länger gerendert

Aber beeilen Sie sich nicht, um Schlussfolgerungen zu ziehen. Schauen wir uns zunächst das Verhalten der Seite in beiden Fällen an.





Wie Sie sehen können, friert die Seite im Normalfall einige Sekunden lang ein und der Benutzer kann in diesem Moment nichts tun, während die Verwendung unserer Pipe mit einem Link zu trackBy dem Benutzer eine fast sofortige Tabelleninitialisierung ermöglicht und keine Beschwerden bei der Verwendung der Anwendung verursacht.

Hoffe dieser Artikel hilft jemandem.

Der Quellcode für die Testanwendung befindet sich auf Stack Blitz .

All Articles