Wie kann ich ngFor für ein großes Array beschleunigen?

9

Ich arbeite an einer Pokedex-Seite und danke, dass es jetzt 721 Pokemon gibt, und ngFor braucht eine lange Zeit, um alles anzuzeigen die Einträge beim ersten Mal. Sobald ich alle Daten geladen habe, dauert es ~ 2400ms, um sie tatsächlich in das DOM zu legen.

Hier ist die ngFor in Frage:

%Vor%

Ich habe in den Dev-Tools von Chrome eine Zeitleiste erstellt und etwas erhalten, das wie folgt aussieht:

Ich habe nicht viel Erfahrung mit der Zeitleiste, aber es scheint mir, dass es einen viel zu großen Block genau dort in der Mitte gibt (der obere ist mit XHR Load (/csv/pokemon_game_indices.csv) gekennzeichnet). Der Ajax-Aufruf selbst dauert 0,02 ms gemäß der Timeline. Ich gehe davon aus, dass die Änderungserkennung, die nach Abschluss der Ajax-Anforderung stattfindet, zu einem so großen Block wird. Das ist, wenn ich meine Modelle nehme, die ich gebaut habe, und sie in die Variable pokedex setze, die der ngFor oben verwendet. Mein Verständnis der Zeitleiste ist, dass die Konstruktion der 721 DOM-Elemente, die von der ngFor hinzugefügt werden sollen, etwa 2.5 Sekunden dauert.

Ich habe versucht, meine entry -Komponente in nur den HTML-Code zu zerlegen (die Komponente tut wirklich nichts), aber das scheint die Zeit nicht merkbar zu beeinflussen. Das Entfernen der Pipe, die ich zum Filtern der Liste verwende, hat keinen Einfluss auf die Zeit.

Gibt es eine Möglichkeit, dieses ngFor zu beschleunigen?

Ich benutze Angular 2 RC1. Ich aktiviere den Prod-Modus. Ich führe dies in Chrome 51.0.2704.79 m

    
Corey Ogburn 02.06.2016, 19:04
quelle

1 Antwort

11

Die kurze und süße Antwort lautet "iteriere nicht über das gesamte Array", aber das war nicht gut genug für mich. Ich wollte, dass es so aussieht, als wäre die gesamte Spalte der Einträge vorhanden. Also setze ich einen Abstandhalter darüber, der ngFor iteriert über einen Teilbereich des Arrays, und ein Abstandhalter darunter und zusammen macht dies die Liste so aussehen wie alle Elemente sind die ganze Zeit da.

Hier ist eine vereinfachte Version meines HTML mit nur den relevanten Teilen zu diesem Problem ( vollständiges Beispiel auf bitbucket ):

%Vor%

Ultra-minimale Struktur für absolute Klarheit:

%Vor%

Zunächst ein sehr wichtiger Punkt: <entry> ist immer genau 120 Pixel hoch mit einem unteren Rand von 12 Pixel, der insgesamt 132 Pixel Platz bietet. Das CSS macht das absolut. Dies funktioniert für jede konstante Größe, die ich wählen wollte, aber ich nehme spezielle Annahmen vor, dass die Größe genau 132 Pixel beträgt.

Die kurze Version besteht darin, dass Sie beim Scrollen der Spalte scrollHeight bestimmen, welche Einträge tatsächlich auf dem Bildschirm angezeigt werden sollen. Wenn die ersten 10 Elemente, die ngFor tatsächlich erstellt, außerhalb des Bildschirms sind, beginnt das erste sichtbare Element mit der Nummer 11. Ich rechne für einen 4k-Bildschirm und zeige 40 Einträge an (nimmt 5280 Pixel auf), um sicherzustellen, dass die gesamte Spalte voll aussieht. Dann, damit die Bildlaufleiste korrekt aussieht, habe ich einen Abstandhalter unterhalb der 40 Einträge, um zu erzwingen, dass die div die richtige scrollbare Höhe hat. Hier ist ein Bild davon, was visuell vor sich geht:

Hier sind die relevanten Variablen und Funktionen im Controller ( bitbucket ):

%Vor%

Es bringt mich um, jQuery hier zu benutzen, aber ich benutzte es bereits für etwas anderes und ich brauchte etwas, das browserübergreifend war. scrollPos enthält den ersten Index des ersten Elements, das ich auf dem Bildschirm anzeigen soll.

Der ngFor, der tatsächlich alle <entry> -Elemente erstellt, sieht folgendermaßen aus:

%Vor%

Das durchbrechen:

base.pokemon ist ein Array der zum Erstellen jedes Eintragselements erforderlichen pokemon-Daten.

... | filter:search:SelectedVer:SelectedLang) wird zum Durchsuchen der Liste verwendet. Ich lasse es hier in meinem Beispiel, um zu zeigen, dass Sie immer noch mit der Liste spielen können, bevor mein Hack ins Spiel kommt.

... | justafew:scrollPos ist der Ort, an dem die Magie passiert. Hier ist der Filter in seiner Gesamtheit ( bitbucket ):

%Vor%

scrollPos wurde als Parameter start übergeben. Wenn ich zum Beispiel 13200 Pixel in meiner Spalte durchgeblättert habe, wäre scrollPos auf 100 gesetzt worden (siehe das Scrollen im obigen Controller). Dadurch wird das Array in Scheiben geschnitten, so dass es die Elemente 90 bis 130 zurückgibt. Ich möchte den Bildschirm ein wenig überlaufen lassen, um sicherzustellen, dass schnelles Scrollen nicht zu sichtbarem Leerraum führt (wahnsinnig schnelles Scrollen könnte es immer noch anzeigen, aber Sie bewegen sich so schnell es ist leicht zu glauben, dass der Browser einfach nicht so schnell gerendert ist, also lasse ich es gleiten). Ich verwende Math.max , also schneide ich nicht mit negativen Zahlen, wie wenn ich ganz oben in der Liste bin und scrollPos ist 0.

Jetzt die Abstandhalter. Sie halten die Bildlaufleiste ehrlich. Ich binde ihre [style.height] und verwende ein wenig Mathe, um diese Abstandshalter den benötigten Platz einnehmen zu lassen. Wenn ich nach unten scrolle, wird der obere Abstandhalter größer und der untere Abstandhalter schrumpft um genau denselben Betrag, so dass die Spalte immer die gleiche Höhe hat. Wenn ich nach oben scrolle, funktioniert die Mathematik genau umgekehrt: die Oberseite schrumpft und die Unterseite wächst. Der untere Spacer verwendet genau die gleiche Filterlogik wie ngFor. Wenn ich also eine Suche durchführe, die 100 statt 721 Pokemon zurückgibt, passt sie sich der Höhe von 100 Einträgen an. Der erste Spacer, der scrollPos - 10 verwendet, weil der justafew -Filter zurückgeht 10. Aus demselben Grund verwendet der untere Spacer scrollPos - 30 , weil das so viele justafew zurückgibt.

Ich weiß, es sieht aus wie viele bewegliche Teile, aber sie sind alle einfach und schnell. Leider gibt es viele "magische Zahlen", die aufeinander angewiesen sind, aber in Anbetracht der Leistungsverbesserungen und der Zuverlässigkeit, die es mir gab, die ganze Liste zu zeigen, ließ ich es gleiten. Vielleicht werde ich eines Tages eine Komponente oder eine Direktive machen, um alles an einen konfigurierbaren Platz zu setzen.

    
Corey Ogburn 31.12.2016, 03:17
quelle

Tags und Links