Ich fange gerade erst mit Knockout.js an (wollte es schon immer mal ausprobieren, aber jetzt habe ich endlich eine Ausrede!) - Ich habe jedoch einige wirklich schlechte Leistungsprobleme, wenn ich einen Tisch an einen relativ kleinen Satz binde Daten (ungefähr 400 Zeilen oder so).
In meinem Modell habe ich folgenden Code:
this.projects = ko.observableArray( [] ); //Bind to empty array at startup
this.loadData = function (data) //Called when AJAX method returns
{
for(var i = 0; i < data.length; i++)
{
this.projects.push(new ResultRow(data[i])); //<-- Bottleneck!
}
};
Das Problem ist, dass die for
obige Schleife mit ungefähr 400 Zeilen ungefähr 30 Sekunden dauert. Wenn ich den Code jedoch in Folgendes ändere:
this.loadData = function (data)
{
var testArray = []; //<-- Plain ol' Javascript array
for(var i = 0; i < data.length; i++)
{
testArray.push(new ResultRow(data[i]));
}
};
Dann for
endet die Schleife im Handumdrehen. Mit anderen Worten, die push
Methode von Knockouts observableArray
Objekt ist unglaublich langsam.
Hier ist meine Vorlage:
<tbody data-bind="foreach: projects">
<tr>
<td data-bind="text: code"></td>
<td><a data-bind="projlink: key, text: projname"></td>
<td data-bind="text: request"></td>
<td data-bind="text: stage"></td>
<td data-bind="text: type"></td>
<td data-bind="text: launch"></td>
<td><a data-bind="mailto: ownerEmail, text: owner"></a></td>
</tr>
</tbody>
Meine Fragen:
- Ist dies der richtige Weg, um meine Daten (die von einer AJAX-Methode stammen) an eine beobachtbare Sammlung zu binden?
- Ich
push
gehe davon aus, dass jedes Mal, wenn ich es aufrufe, eine gründliche Neuberechnung durchgeführt wird, z. B. das Wiederherstellen gebundener DOM-Objekte. Gibt es eine Möglichkeit, diese Neuberechnung zu verzögern oder alle meine Artikel gleichzeitig einzuschieben?
Ich kann bei Bedarf weiteren Code hinzufügen, bin mir aber ziemlich sicher, dass dies relevant ist. Zum größten Teil habe ich nur Knockout-Tutorials von der Site verfolgt.
AKTUALISIEREN:
Gemäß den folgenden Hinweisen habe ich meinen Code aktualisiert:
this.loadData = function (data)
{
var mappedData = $.map(data, function (item) { return new ResultRow(item) });
this.projects(mappedData);
};
Es this.projects()
dauert jedoch immer noch ungefähr 10 Sekunden für 400 Zeilen. Ich gebe zu, ich bin mir nicht sicher, wie schnell dies ohne Knockout wäre (nur Zeilen durch das DOM hinzufügen), aber ich habe das Gefühl, es wäre viel schneller als 10 Sekunden.
UPDATE 2:
Gemäß den folgenden Ratschlägen habe ich jQuery.tmpl ausprobiert (was von KnockOut nativ unterstützt wird), und diese Template-Engine zeichnet in etwas mehr als 3 Sekunden etwa 400 Zeilen. Dies scheint der beste Ansatz zu sein, kurz vor einer Lösung, bei der beim Scrollen dynamisch mehr Daten geladen werden.
quelle
valueHasMutated
macht es. Überprüfen Sie die Antwort, wenn Sie Zeit haben.Antworten:
Wie in den Kommentaren vorgeschlagen.
Knockout verfügt über eine eigene native Template-Engine, die den (foreach, with) -Bindungen zugeordnet ist. Es unterstützt auch andere Template-Engines, nämlich jquery.tmpl. Lesen Sie hier für weitere Details. Ich habe kein Benchmarking mit verschiedenen Motoren durchgeführt, weiß also nicht, ob es helfen wird. Wenn Sie Ihren vorherigen Kommentar lesen, haben Sie in IE7 möglicherweise Schwierigkeiten, die gewünschte Leistung zu erzielen.
Abgesehen davon unterstützt KO jede js-Template-Engine, wenn jemand den Adapter dafür geschrieben hat. Vielleicht möchten Sie andere ausprobieren, da jquery tmpl durch JsRender ersetzt werden soll .
quelle
jquery.tmpl
also werde ich das nutzen. Ich könnte andere Motoren untersuchen und meine eigenen schreiben, wenn ich etwas mehr Zeit habe. Vielen Dank!data-bind
Anweisungen in Ihrer jQuery-Vorlage oder verwenden Sie die Syntax $ {code}?${code}
Syntax und es ist viel schneller. Ich habe auch versucht, Underscore.js<% .. %>
zum Laufen zu bringen, hatte aber noch kein Glück (die Syntax stört ASP.NET), und es scheint noch keine JsRender-Unterstützung zu geben.ResultRow
, wird die Benutzeroberfläche nicht aktualisiert (Sie müssen dasprojects
ObservableArray aktualisieren, wodurch ein erneutes Rendern Ihrer Tabelle erzwungen wird). $ {} kann definitiv vorteilhaft sein, wenn Ihre Daten so gut wie schreibgeschützt sindSiehe: Knockout.js Performance Gotcha # 2 - Manipulieren von ObservableArrays
quelle
Verwenden Sie die Paginierung mit KO zusätzlich zu $ .map.
Ich hatte das gleiche Problem mit großen Datensätzen von 1400 Datensätzen, bis ich Paging mit Knockout verwendete. Die Verwendung
$.map
zum Laden der Datensätze machte einen großen Unterschied, aber die DOM-Renderzeit war immer noch abscheulich. Dann habe ich versucht, die Paginierung zu verwenden, und dadurch wurde meine Datensatzbeleuchtung schnell und benutzerfreundlicher. Eine Seitengröße von 50 machte den Datensatz weniger überwältigend und reduzierte die Anzahl der DOM-Elemente dramatisch.Mit KO ist das sehr einfach:
http://jsfiddle.net/rniemeyer/5Xr2X/
quelle
KnockoutJS bietet einige großartige Tutorials, insbesondere zum Laden und Speichern von Daten
In ihrem Fall ziehen sie Daten,
getJSON()
die extrem schnell sind. Aus ihrem Beispiel:quelle
self.tasks(mappedTasks)
Ausführung dauert ungefähr 10 Sekunden (mit 400 Zeilen). Ich denke, das ist immer noch nicht akzeptabel.+1
um meinen Code zu vereinfachen und die Geschwindigkeit dramatisch zu erhöhen. Vielleicht hat jemand eine detailliertere Erklärung, was der Engpass ist.Schauen Sie sich KoGrid an . Es verwaltet Ihr Zeilen-Rendering intelligent, sodass es leistungsfähiger ist.
Wenn Sie versuchen, 400 Zeilen mithilfe einer
foreach
Bindung an eine Tabelle zu binden, werden Sie Probleme haben, so viel durch KO in das DOM zu übertragen.KO macht einige sehr interessante Dinge mit der
foreach
Bindung, von denen die meisten sehr gute Operationen sind, aber sie beginnen mit der Größe zusammenzufallen, wenn die Größe Ihres Arrays zunimmt.Ich war auf dem langen, dunklen Weg, große Datenmengen an Tabellen / Gitter zu binden, und am Ende müssen Sie die Daten lokal aufteilen / seiten.
KoGrid macht das alles. Es wurde erstellt, um nur die Zeilen zu rendern, die der Betrachter auf der Seite sehen kann, und um dann die anderen Zeilen zu virtualisieren, bis sie benötigt werden. Ich denke, Sie werden feststellen, dass die Leistung bei 400 Artikeln viel besser ist als bei Ihnen.
quelle
Eine Lösung, um zu vermeiden, dass der Browser beim Rendern eines sehr großen Arrays blockiert wird, besteht darin, das Array so zu drosseln, dass nur wenige Elemente gleichzeitig hinzugefügt werden, wobei dazwischen ein Ruhezustand liegt. Hier ist eine Funktion, die genau das tut:
Abhängig von Ihrem Anwendungsfall kann dies zu einer massiven UX-Verbesserung führen, da der Benutzer möglicherweise nur den ersten Zeilenstapel sieht, bevor er einen Bildlauf durchführen muss.
quelle
Push () zu nutzen, um variable Argumente zu akzeptieren, ergab in meinem Fall die beste Leistung. 1300 Reihen wurden für 5973 ms (~ 6 s) geladen. Mit dieser Optimierung wurde die Ladezeit auf 914 ms (<1 Sek.) Reduziert.
Das ist eine Verbesserung von 84,7%!
Weitere Informationen finden Sie unter Verschieben von Elementen in ein beobachtbares Array
quelle
Ich hatte es mit so großen Datenmengen zu tun, dass es für mich
valueHasMutated
wie ein Zauber war.Modell anzeigen:
Nach dem Aufruf des
(4)
Arrays werden die Datenthis.projects
automatisch in das erforderliche ObservableArray geladen .Wenn Sie Zeit haben, schauen Sie sich das an und lassen Sie es mich wissen, falls es Probleme gibt
Trick hier: Auf diese Weise können Abhängigkeiten (berechnet, abonniert usw.) auf Push-Ebene vermieden werden und nach dem Aufruf auf einmal ausgeführt werden
(4)
.quelle
push
, das Problem ist, dass selbst ein einzelner Aufruf zum Pushen lange Renderzeiten verursacht. Wenn in einem Array 1000 Elemente an a gebunden sind,foreach
wird durch Drücken eines einzelnen Elements das gesamte Foreach erneut gerendert, und Sie zahlen hohe Kosten für die Renderzeit.Eine mögliche Problemumgehung in Kombination mit der Verwendung von jQuery.tmpl besteht darin, Elemente mithilfe von setTimeout asynchron auf das beobachtbare Array zu übertragen.
Auf diese Weise kann sich der Browser / knockout.js die Zeit nehmen, um das DOM entsprechend zu bearbeiten, wenn Sie jeweils nur ein einzelnes Element hinzufügen, ohne dass der Browser für einige Sekunden vollständig blockiert wird, sodass der Benutzer gleichzeitig durch die Liste scrollen kann.
quelle
Ich habe mit Leistung experimentiert und habe zwei Beiträge, von denen ich hoffe, dass sie nützlich sind.
Meine Experimente konzentrieren sich auf die DOM-Manipulationszeit. Bevor Sie darauf eingehen, sollten Sie unbedingt die obigen Punkte zum Verschieben in ein JS-Array befolgen, bevor Sie ein beobachtbares Array usw. erstellen.
Wenn Ihnen die DOM-Manipulationszeit immer noch im Weg steht, kann dies helfen:
1: Ein Muster, mit dem ein Ladespinner um das langsame Rendern gewickelt und dann mit afterRender ausgeblendet wird
http://jsfiddle.net/HBYyL/1/
Dies ist keine wirkliche Lösung für das Leistungsproblem, zeigt jedoch, dass eine Verzögerung wahrscheinlich unvermeidlich ist, wenn Sie Tausende von Elementen durchlaufen und ein Muster verwenden, mit dem Sie sicherstellen können, dass vor dem langen KO-Vorgang ein Ladespinner angezeigt und dann ausgeblendet wird es danach. So verbessert es zumindest die UX.
Stellen Sie sicher, dass Sie einen Spinner laden können:
Verstecke den Spinner:
was auslöst:
2: Verwenden der HTML-Bindung als Hack
Ich erinnerte mich an eine alte Technik aus der Zeit, als ich mit Opera an einer Set-Top-Box arbeitete und die Benutzeroberfläche mithilfe der DOM-Manipulation erstellte. Es war entsetzlich langsam, daher bestand die Lösung darin, große HTML-Blöcke als Zeichenfolgen zu speichern und die Zeichenfolgen durch Festlegen der innerHTML-Eigenschaft zu laden.
Ähnliches kann erreicht werden, indem die HTML-Bindung und eine Berechnung verwendet werden, die den HTML-Code für die Tabelle als großen Textblock ableitet und ihn dann auf einmal anwendet. Dies behebt zwar das Leistungsproblem, aber der massive Nachteil besteht darin, dass die Möglichkeiten zum Binden in jeder Tabellenzeile stark eingeschränkt werden.
Hier ist eine Geige, die diesen Ansatz zeigt, zusammen mit einer Funktion, die aus den Tabellenzeilen heraus aufgerufen werden kann, um ein Element auf vage KO-ähnliche Weise zu löschen. Natürlich ist dies nicht so gut wie ein richtiger KO, aber wenn Sie wirklich eine hervorragende Leistung benötigen, ist dies eine mögliche Problemumgehung.
http://jsfiddle.net/9ZF3g/5/
quelle
Wenn Sie den IE verwenden, schließen Sie die Entwicklertools.
Wenn die Entwicklertools im IE geöffnet sind, wird dieser Vorgang erheblich verlangsamt. Ich füge einem Array ~ 1000 Elemente hinzu. Wenn die Entwicklungswerkzeuge geöffnet sind, dauert dies ungefähr 10 Sekunden und der IE friert ein, während dies geschieht. Wenn ich die Entwickler-Tools schließe, ist der Vorgang sofort und ich sehe keine Verlangsamung im IE.
quelle
Mir ist auch aufgefallen, dass die Knockout js Template Engine im IE langsamer arbeitet. Ich habe sie durch underscore.js ersetzt und arbeite viel schneller.
quelle