Was ist der richtige Weg, um eine Karteninstanz zu zerstören?

89

Ich habe kürzlich eine mobile HTML5-Anwendung entwickelt. Die Anwendung war eine einzelne Seite, auf der Navigations-Hash-Änderungsereignisse das gesamte DOM ersetzten. Ein Abschnitt der Anwendung war eine Google Map mit API v3. Bevor das Map-Div aus dem DOM entfernt wird, möchte ich alle Event-Handler / Listener entfernen und so viel Speicher wie möglich freigeben, da der Benutzer möglicherweise nicht mehr zu diesem Abschnitt zurückkehrt.

Was ist der beste Weg, um eine Karteninstanz zu zerstören?

Chad Killingsworth
quelle
Verwandte Frage (2014): stackoverflow.com/questions/21142483/…
kashiraja
Code für den Versuch, alle Ereignis-Listener auf einer Karte zu entfernen, Google Maps-Fehler 35821412
Kashiraja

Antworten:

48

Ich füge eine zweite Antwort auf diese Frage hinzu, da ich das Hin und Her, das wir durch nachfolgende Kommentare zu meiner vorherigen Antwort hatten, nicht entfernen möchte.

Aber ich bin kürzlich auf einige Informationen gestoßen, die Ihre Frage direkt ansprechen, und deshalb wollte ich sie teilen. Ich weiß nicht, ob Sie sich dessen bewusst sind, aber während der Videozeiten von Google Maps API am 9. Mai 2012 haben Chris Broadfoot und Luke Mahe von Google genau diese Frage aus dem Stackoverflow besprochen. Wenn Sie die Videowiedergabe auf 12:50 einstellen, wird in diesem Abschnitt Ihre Frage besprochen.

Im Wesentlichen geben sie zu, dass es sich um einen Fehler handelt, fügen aber auch hinzu, dass sie Anwendungsfälle, bei denen aufeinanderfolgende Karteninstanzen erstellt / zerstört werden, nicht wirklich unterstützen. Es wird dringend empfohlen, eine einzelne Instanz der Karte zu erstellen und in jedem Szenario dieser Art wiederzuverwenden. Sie sprechen auch darüber, die Map auf null zu setzen und Ereignis-Listener explizit zu entfernen. Sie haben Bedenken hinsichtlich der Ereignis-Listener geäußert. Ich dachte, es würde ausreichen, nur die Karte auf Null zu setzen, aber es sieht so aus, als wären Ihre Bedenken berechtigt, da sie speziell Ereignis-Listener erwähnen. Sie empfahlen auch, den DIV, der auch die Karte enthält, vollständig zu entfernen.

Auf jeden Fall wollte ich dies nur weitergeben und sicherstellen, dass es in der Stackoverflow-Diskussion enthalten ist, und hoffe, dass es Ihnen und anderen hilft.

Sean Mickey
quelle
2
Danke - ich habe sie gebeten, die Frage zu den Bürozeiten zu beantworten, aber noch keine Gelegenheit gehabt, das Video zu überprüfen.
Chad Killingsworth
Nun, Sie hätten einfach die vorherige Antwort aktualisieren können und erwähnen können, dass es sich um ein Update handelt ...
TJ
4
Nun, großartig. Es ist 2018 und es scheint immer noch keinen Weg zu geben, dies zu tun.
JP Silvashy
28

Die offizielle Antwort lautet: Nicht. Karteninstanzen in einer Einzelseitenanwendung sollten wiederverwendet und nicht zerstört und dann neu erstellt werden.

Für einige Anwendungen mit nur einer Seite kann dies bedeuten, dass die Lösung so umgestaltet wird, dass eine erstellte Karte nach dem Erstellen möglicherweise ausgeblendet oder vom DOM getrennt wird, jedoch niemals zerstört / neu erstellt wird.

Chad Killingsworth
quelle
Das ist sehr, sehr schlecht - ich habe eine mehrsprachige einseitige Anwendung und möchte Google Map in der ausgewählten Sprache anzeigen.
Artuska
14

Da Sie anscheinend Karteninstanzen nicht wirklich zerstören können, können Sie dieses Problem reduzieren, wenn

  • Sie müssen mehrere Karten gleichzeitig auf einer Website anzeigen
  • Die Anzahl der Karten kann sich mit der Benutzerinteraktion ändern
  • Die Karten müssen ausgeblendet und zusammen mit anderen Komponenten erneut angezeigt werden (dh sie werden nicht an einer festen Position im DOM angezeigt).

hält einen Pool von Karteninstanzen. Der Pool verfolgt die verwendeten Instanzen und prüft, wenn eine neue Instanz angefordert wird, ob eine der verfügbaren Karteninstanzen frei ist. Wenn dies der Fall ist, wird eine vorhandene Instanz zurückgegeben. Wenn dies nicht der Fall ist, wird eine erstellt neue Karteninstanz und geben Sie sie zurück und fügen Sie sie dem Pool hinzu. Auf diese Weise haben Sie nur eine maximale Anzahl von Instanzen, die der maximalen Anzahl von Karten entspricht, die Sie jemals gleichzeitig auf dem Bildschirm anzeigen. Ich verwende diesen Code (es erfordert jQuery):

var mapInstancesPool = {
 pool: [],
 used: 0,
 getInstance: function(options){
    if(mapInstancesPool.used >= mapInstancesPool.pool.length){
        mapInstancesPool.used++;
        mapInstancesPool.pool.push (mapInstancesPool.createNewInstance(options));
    } else { 
        mapInstancesPool.used++;
    }
    return mapInstancesPool.pool[mapInstancesPool.used-1];
 },
 reset: function(){
    mapInstancesPool.used = 0;
 },
 createNewInstance: function(options){
    var div = $("<div></div>").addClass("myDivClassHereForStyling");
    var map =   new google.maps.Map(div[0], options);
    return {
        map: map,
        div: div
    }
 }
}

Sie übergeben ihm die Startkartenoptionen (gemäß dem zweiten Argument des Konstruktors von google.maps.Map) und es wird sowohl die Karteninstanz (auf der Sie Funktionen für google.maps.Map aufrufen können) als auch der Container zurückgegeben Sie können mit der Klasse "myDivClassHereForStyling" stylen und an das DOM anhängen. Wenn Sie das System zurücksetzen müssen, können Sie mapInstancesPool.reset () verwenden. Der Zähler wird auf 0 zurückgesetzt, während alle vorhandenen Instanzen im Pool zur Wiederverwendung aufbewahrt werden. In meiner Anwendung musste ich alle Karten auf einmal entfernen und einen neuen Kartensatz erstellen, damit es keine Funktion zum Recyceln einer bestimmten Karteninstanz gibt: Ihr Kilometerstand kann variieren. Um die Karten vom Bildschirm zu entfernen, verwende ich jQuerys Detach, wodurch der Container der Karte nicht zerstört wird.

Mit diesem System und mit

google.maps.event.clearInstanceListeners(window);
google.maps.event.clearInstanceListeners(document);

und läuft

google.maps.event.clearInstanceListeners(divReference[0]);
divReference.detach()

(wobei divReference das jQuery-Objekt des div ist, das aus dem Instanzpool zurückgegeben wird) Bei jedem Div, das ich entferne, konnte ich die Speichernutzung von Chrome mehr oder weniger stabil halten, anstatt sie jedes Mal zu erhöhen, wenn ich Karten lösche und neue hinzufüge.

Paolo Mioni
quelle
Du hast mein Leben gerettet! Danke;)
Felipe Desiderati
Freut mich zu helfen !!
Paolo Mioni
5

Ich hätte vorgeschlagen, den Inhalt des Map-Div zu entfernen und deletedie Variable zu verwenden, die den Verweis auf die Map enthält, und wahrscheinlich deletealle Ereignis-Listener explizit zu verwenden .

Es gibt jedoch einen anerkannten Fehler , der möglicherweise nicht funktioniert.

Andrew Leach
quelle
Dies ist eine gute Diskussion. Ich denke nicht, dass das Anrufen deleteviel hinzufügt (siehe stackoverflow.com/q/742623/1314132 ), aber es kann wirklich nicht schaden. Am Ende kommt es auf die Frage an: Gibt es Verweise auf das Objekt? Wenn ja, wird kein Müll gesammelt.
Sean Mickey
1
@ SeanMickey: Hier wird der Fehler relevant. Version 2 muss GUnload()alle internen Referenzen der API entfernen.
Andrew Leach
Ich habe mit dieser Seite in Chrome getestet : people.missouristate.edu/chadkillingsworth/mapsexamples/… Bisher sinkt die Speichernutzung nach dem Entfernen der Karte nur geringfügig, liegt jedoch bei weitem nicht in der Nähe des Niveaus, bevor die Karte instanziiert wird.
Chad Killingsworth
@ AndrewLeach Absolut. Aber wenn sie einen Fehler haben, der einen Speicherverlust verursacht, können wir nicht viel tun, bis er behoben ist. Ich meine, wenn es nicht funktioniert, alle Kartenobjekte nicht erreichbar zu machen, deleteist das keine wirkliche Lösung. Sie müssen das Problem beheben, damit die Referenzen nicht mehr erreichbar sind, oder eine neue Funktion hinzufügen, die die von Ihnen beschriebene Funktionalität bietet GUnload().
Sean Mickey
1
Chad / Andrew: Ja, ich habe dieses Problem leider reproduziert deleteund das Löschen des innerHTMLnicht vollständig gelöschten Speichers. Leider ist es kein Fehler mit hoher Priorität.
Chris Broadfoot
2

Da Google gunload () für API v3 nicht bereitstellt, verwenden Sie Iframe besser in HTML und weisen Sie diesem Iframe map.html als Quelle zu. Nach Gebrauch mache src als null. Dadurch wird definitiv der von der Karte verbrauchte Speicher freigegeben.

Kumar
quelle
2
Jede Instanz des Iframes müsste dann die Maps-API neu laden, was nicht ideal ist.
Chad Killingsworth
1

Wenn Sie das entfernen div, wird das Anzeigefeld entfernt und die Karte verschwindet. Um die Karteninstanz zu entfernen, stellen Sie einfach sicher, dass Ihr Verweis auf die Karte auf nullund alle Verweise auf andere Teile der Karte auf gesetzt sind null. Zu diesem Zeitpunkt kümmert sich die JavaScript-Speicherbereinigung um die Bereinigung, wie in: Wie funktioniert die Speicherbereinigung in JavaScript? .

Sean Mickey
quelle
1
Ich bin nicht sicher, ob das Setzen der Map-Variablen auf null alle Ereignis-Listener ordnungsgemäß entfernt.
Chad Killingsworth
1
Es muss nicht nur die Karte eingestellt werden null, sondern auch Verweise auf etwas anderes. Wenn die Markierungsreferenz auf gesetzt nullist und nicht erreichbar ist, kann der Ereignis-Listener nicht erreicht werden. Es ist möglicherweise immer noch mit der Karte verbunden, aber die Karte kann nicht erreicht werden. Es handelt sich also nur um einen großen Teil des Speichers, der im Wesentlichen verwaist ist. Es ist das gleiche wie das Setzen eines Array.length = 0; Wenn keine weiteren Verweise auf die Mitglieder vorhanden sind, bilden sie lediglich eine Gruppe verwaister Speicher, die für die Speicherbereinigung berechtigt sind.
Sean Mickey
0

Ich denke du redest addEventListener. Wenn Sie die DOM-Elemente entfernen, verlieren einige Browser diese Ereignisse und entfernen sie nicht. Aus diesem Grund führt jQuery beim Entfernen eines Elements verschiedene Aktionen aus:

  • Es entfernt die Ereignisse, wenn es verwendet werden kann removeEventListener. Das bedeutet, dass ein Array mit den Ereignis-Listenern beibehalten wird, die es zu diesem Element hinzugefügt hat.
  • Er löscht die Attribute über Ereignisse ( onclick, onblur, usw.) unter Verwendung von deleteauf dem DOM - Elemente , wenn addEventListenernicht verfügbar ist (still, es hat eine Anordnung , wo es die Ereignisse speichert hinzugefügt).
  • Es setzt das Element so null, dass Speicherlecks im IE 6/7/8 vermieden werden.
  • Anschließend wird das Element entfernt.
Florian Margaine
quelle
Ich beziehe mich hauptsächlich auf die internen Google Maps API-Ereignisse. Sie können mithilfe der unter developer.google.com/maps/documentation/javascript/… dokumentierten API-Ereignismethoden hinzugefügt / entfernt / ausgelöst werden . Obwohl die Funktionalität dem Browser addEventListener ähnlich ist, gibt es eine große Anzahl von benutzerdefinierten Ereignissen, die für die Karte spezifisch sind (z. B. "bounds_changed"), und einige dieser Ereignishandler hängen an den Browserereignissen wie dem Ereignis "Größe ändern" der Karte an.
Chad Killingsworth
Behalten Sie dann ein Array der hinzugefügten Ereignisse bei und entfernen Sie es dann manuell mithilfe removeEventListeneroder deleteabhängig von der Art des Ereignisses.
Florian Margaine