Go! Next ist eines der Produkte, welche wir im Team Brokerage entwickeln. Es ist die nächste Generation unserer Engel & Völkers Web App Kernanwendungen, welche die Immobilienberater nutzen können.
Die Web-App befindet sich bereits einige Monate in einer Pilotphase, in der Immobilienberater hauptsächlich in Spanien, Frankreich, Italien und Deutschland sie testen, welche diese Software zur Unterstützung ihres Tagesgeschäftes einsetzen.
Zur Erstellung unseres CRMs, haben wir AngularJS framework, ein Frontend-Framework für MVW (Model View Whatever) verwendet.
Mit dem zunehmenden Bedarf nach weiteren Features, sahen wir uns mit einer schlechten Performance im Frontend konfrontiert, die zu ernsten Verzögerungen und sogar zu einem Crash der Software führte.
In diesem Beitrag werde ich zwei der Hauptgründe, Speicherlecks und AngularJS-Digest-Zyklus, welche wir angepasst haben, um die Leistung unserer Web-App zu verbessern, beleuchten.
Die Aufzeichnung der runtime Performance (mithilfe von Chrome dev tools) während laufender Interaktionen auf der Benutzeroberfläche zeigte, dass die JS-Heap-Größe und die Anzahl der HTML-Nodes auch bei erzwungenen garbage collections erhöht wurden.
Da wir viele benutzerdefinierte Directives und Overlays verwenden, mussten wir sicherstellen, dass sich jede Direktive selbst abschließend bereinigt und jedes geschlossene Overlay (des entsprechenden Controllers) seinen $ scope bereinigt.
Hier ist $scope. $destroy sehr hilfreich!!
Aber zunächst müssen wir in AngularJS zwischen zwei Arten von "Event Listeners" unterscheiden:
Wenn $scope.$destroy() ausgeführt wird, werden alle Listener gelöscht, die über $on auf diesem $scope. registriert sind. Es werden jedoch weder DOM-Elemente noch angehängte Event Handlers entfernt.
Um also mit der zweiten Art von Listenern umgehen zu können, müssen wir element.remove() verwenden. Wenn element.remove () ausgeführt wird, werden dieses Element und alle untergeordneten Elemente aus dem DOM-Baum sowie allen angehängten Event Handlers (z. B. element.on) entfernt. Es wird jedoch nicht den $scope der mit dem Element verknüpft ist entfernen.
Die Kombination von $scope.$destroy() und element.remove() stellt das Löschen beider Listener-Arten sicher. Darüber hinaus sollten Event Handlers, die an Elemente außerhalb der Directive angehängt sind, manuell bereinigt werden.
Dasselbe gilt für registrierte Listener in $ rootScope:
Dies ist erforderlich, da der $ rootScope während der Laufzeit der Anwendung niemals zerstört wird.
Eine weitere Möglichkeit ist $interval und $timeout abzubrechen, da beide promises zurückgeben:
Nach den Änderungen zeigte die Aufzeichnung einer Perfomance für dieselben Benutzerinteraktionen eine bessere Speicherverwaltung:
Das AngularJS-Framework verfügt über ein hervorragendes Feature: two way data binding, d.h., das Modell kann vom Controller oder von der View aus aktualisiert werden. Dies wird durch Watcher erreicht: jede an den View gebundene Variable / Expression wird "überwacht" und wenn ein Digest-Zyklus ausgelöst wird, iteriert AngularJS über alle Watcher, die eine Dirty-Prüfung ausführen und rendert den View basierend auf aktualisierten Modellen neu.
Mit Wachstum der Anwendung erhöhen sich die Anzahl der Bindungen und die Größe unserer $digest Loops. Dies beeinträchtigt unsere Leistung, wenn wir eine große Menge an Bindungen pro Application-View haben. Leider war dies bei uns der Fall. Wir hatten Tausende von Watchern, die einen längeren Ladezyklus verursachten.
Um dieses Problem zu lösen, haben wir viele Techniken verwendet:
Unser Weg zur Verbesserung unserer CRM-Leistung geht täglich weiter. Der nächste Schritt wird die Optimierung der Bilder und somit der Seitenladezeiten sein. Wir werden Euch auf dem Laufenden halten :)
References