Blog > News > Software Engineering > Die Performance zählt!

Die Performance zählt!

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.

 Hamburg
- Chromecrash

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.

Speicherlecks

Wir entschieden uns in der App Overlays zu verwenden, um den Benutzer in seinem Tagesgeschäft zu unterstützen.
 Hamburg
- GoNext!

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.

 Hamburg
- runtime Performance

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!!

 Hamburg
- Engel & Völkers Technology

Aber zunächst müssen wir in AngularJS zwischen zwei Arten von "Event Listeners" unterscheiden:

  •  Scope Event Listener, die über $on registriert wurden:
  • Event handlers, die mit Hilfe von  on oder bind an Elemente angehängt sind:

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:

 Hamburg
- Speicherlecks

AngularJS-Verdau


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.

 Hamburg
- AngularJS official documentation

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.



 Hamburg
- Watcher

Um dieses Problem zu lösen, haben wir viele Techniken verwendet:


  • One-time Datenbindung:
    Aus AngularJS docs "One-time expressions will stop recalculating once they are stable, which happens after the first digest if the expression result is a non-undefined value." Wenn wir einen Wert wie  {{::foo }} (anstelle von {{ foo }}) innerhalb des DOM deklarieren, wird AngularJS, sobald dieser Wert definiert ist, ihn aus der Wachter-Liste entfernen und somit in der  $digest
    -Schleife die Anzahl der Bindungen reduzieren.


  • Verwenden von ng-if anstelle von ng-show:
    Beide Direktiven verbergen / zeigen HTML-Inhalt in der Ansicht, aber der Hauptunterschied ist, dass ng-show CSS property display:hidden dem Element hinzufügt (und somit auch seinen Kindern), um es auszublenden, während ng-if das element aus dem DOM entfernt. Durch das Entfernen des Elements und aller untergeordneten Elemente wird die Registrierung aller Watcher aufgehoben.

  • Filter in der Steuerung, wenn möglich: Wir verwenden Stateful-Filter speziell für die Übersetzung, die von AngularJS nicht optimiert werden können. Daher haben wir darauf geachtet, alle möglichen Filter von der View auf den Controller zu verschieben.

  • Endloses Scrollen durch Paginierung ersetzen: Die Verwendung von ng-repeat mit großen Arrays hatte dramatische Auswirkungen auf die Leistung unserer App, insbesondere bei der unendlichen Bildlauffunktion. Wir haben uns dazu entschlossen, es durch eine Paginierung zu ersetzen, um die Kontrolle über die Anzahl der Watcher zu behalten.
 Hamburg
- Screen Shot

*The result

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 :)

 Hamburg
- Software Engineer Engel & Völkers Technology
Kontaktieren Sie uns jetzt
Engel & Völkers
Technology
  • Vancouverstraße 2a
    20457 Hamburg
    Deutschland
  • Telefon 040 36131-0

Folgen Sie uns auf Social Media