Qual è il modo corretto per distruggere un'istanza della mappa?


89

Recentemente ho sviluppato un'applicazione mobile html5. L'applicazione era una singola pagina in cui gli eventi di modifica hash di navigazione sostituivano l'intero DOM. Una sezione dell'applicazione era una mappa di Google che utilizzava l'API v3. Prima che il div della mappa venga rimosso dal DOM, desidero rimuovere tutti i gestori / ascoltatori di eventi e liberare quanta più memoria possibile poiché l'utente potrebbe non tornare più a quella sezione.

Qual è il modo migliore per distruggere un'istanza della mappa?



Codice per tentare di rimuovere tutti i listener di eventi su una mappa, bug di Google Maps 35821412
kashiraja

Risposte:


48

Aggiungo una seconda risposta a questa domanda, perché non voglio rimuovere il avanti e indietro che avevamo tramite commenti di follow-up sulla mia risposta precedente.

Ma recentemente mi sono imbattuto in alcune informazioni che rispondono direttamente alla tua domanda e quindi ho voluto condividere. Non so se sei a conoscenza di questo, ma durante il video Office Hours dell'API di Google Maps del 9 maggio 2012 , Chris Broadfoot e Luke Mahe di Google hanno discusso proprio questa domanda da stackoverflow. Se imposti la riproduzione del video su 12:50, quella è la sezione in cui discutono la tua domanda.

In sostanza, ammettono che si tratta di un bug, ma aggiungono anche che non supportano realmente i casi d'uso che implicano la creazione / distruzione di istanze di mappe successive. Raccomandano vivamente di creare una singola istanza della mappa e di riutilizzarla in qualsiasi scenario di questo tipo. Parlano anche dell'impostazione della mappa su null e della rimozione esplicita dei listener di eventi. Hai espresso preoccupazione per i listener di eventi, ho pensato che sarebbe stato sufficiente impostare la mappa su null, ma sembra che le tue preoccupazioni siano valide, perché menzionano specificamente i listener di eventi. Hanno anche consigliato di rimuovere completamente il DIV che contiene anche la mappa.

In ogni caso, volevo solo passare questo e assicurarmi che sia incluso nella discussione di stackoverflow e spero che aiuti te e gli altri-


2
Grazie - ho chiesto loro di affrontare la domanda durante l'orario di ufficio ma non avevo ancora avuto la possibilità di controllare il video.
Chad Killingsworth

Beh, avresti potuto semplicemente aggiornare la risposta precedente menzionando che si tratta di un aggiornamento ...
TJ

4
Bene, fantastico ... è il 2018 e non sembra ancora esserci un modo per farlo.
JP Silvashy

28

La risposta ufficiale è che non lo fai. Le istanze della mappa in un'applicazione a pagina singola dovrebbero essere riutilizzate e non distrutte, quindi ricreate.

Per alcune applicazioni a pagina singola, ciò potrebbe significare riprogettare la soluzione in modo tale che una volta creata una mappa possa essere nascosta o scollegata dal DOM, ma non viene mai distrutta / ricreata.


Questo è molto molto brutto: ho un'applicazione multilingua a pagina singola e voglio visualizzare Google Map nella lingua selezionata.
artuska

14

Poiché a quanto pare non puoi davvero distruggere le istanze della mappa, un modo per ridurre questo problema se

  • è necessario mostrare più mappe contemporaneamente su un sito web
  • il numero di mappe può cambiare con l'interazione dell'utente
  • le mappe devono essere nascoste e mostrate nuovamente insieme ad altri componenti (ovvero non compaiono in una posizione fissa nel DOM)

sta mantenendo un pool di istanze della mappa. Il pool tiene traccia delle istanze in uso, e quando viene richiesta una nuova istanza, controlla se qualcuna delle istanze di mappa disponibili è libera: se lo è, ne restituirà una esistente, se non lo è, creerà un nuova istanza della mappa e restituirla, aggiungendola al pool. In questo modo avrai solo un numero massimo di istanze pari al numero massimo di mappe che visualizzi contemporaneamente sullo schermo. Sto usando questo codice (richiede 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
    }
 }
}

Gli si passano le opzioni della mappa iniziale (come per il secondo argomento del costruttore di google.maps.Map) e restituisce sia l'istanza della mappa (su cui è possibile chiamare funzioni relative a google.maps.Map), sia il contenitore, che puoi applicare lo stile usando la classe "myDivClassHereForStyling" e puoi aggiungere dinamicamente al DOM. Se è necessario ripristinare il sistema, è possibile utilizzare mapInstancesPool.reset (). Reimposta il contatore a 0, mantenendo tutte le istanze esistenti nel pool per il riutilizzo. Nella mia applicazione avevo bisogno di rimuovere tutte le mappe contemporaneamente e creare un nuovo set di mappe, quindi non c'è alcuna funzione per riciclare un'istanza di mappa specifica: il tuo chilometraggio può variare. Per rimuovere le mappe dallo schermo, utilizzo il distacco di jQuery, che non distrugge il contenitore della mappa.

Utilizzando questo sistema e utilizzando

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

e in esecuzione

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

(dove divReference è l'oggetto jQuery del div restituito dal pool di istanze) su ogni div che rimuovo, sono riuscito a mantenere l'utilizzo della memoria di Chrome più o meno stabile, invece di aumentare ogni volta che elimino mappe e ne aggiungo di nuove.


HAI SALVATO LA MIA VITA! Grazie;)
Felipe Desiderati

Felice di essere d'aiuto !!
Paolo Mioni

5

Avrei suggerito di rimuovere il contenuto del div della mappa e di utilizzare deletesulla variabile che contiene il riferimento alla mappa, e probabilmente di deleteingaggiare esplicitamente eventuali listener di eventi.

Tuttavia, esiste un bug riconosciuto e potrebbe non funzionare.


Questa è una buona discussione. Non penso che chiamare deleteaggiunga molto (vedi stackoverflow.com/q/742623/1314132 ), ma davvero non può far male. Alla fine, si arriva a questa domanda: ci sono riferimenti all'oggetto? In caso affermativo, non verranno raccolti i rifiuti.
Sean Mickey

1
@SeanMickey: che è dove il bug diventa rilevante. La versione 2 deve GUnload()rimuovere tutti i riferimenti interni dell'API.
Andrew Leach

Ho provato con questa pagina in Chrome: people.missouristate.edu/chadkillingsworth/mapsexamples/… Finora l'utilizzo della memoria dopo la rimozione della mappa diminuisce solo leggermente, ma non è neanche lontanamente vicino al livello prima che la mappa venga istanziata.
Chad Killingsworth

@AndrewLeach Assolutamente. Ma se hanno un bug che causa una perdita di memoria, non c'è molto che possiamo fare fino a quando non viene risolto. Voglio dire, se rendere irraggiungibili tutti gli oggetti della mappa non funziona, allora deletenon è davvero una soluzione. Devono risolvere il problema in modo che rendere i riferimenti irraggiungibili funzioni come dovrebbe o aggiungere una nuova funzione che fornisca la funzionalità per cui descrivi GUnload().
Sean Mickey

1
Chad / Andrew: sì, ho riprodotto questo problema, sfortunatamente deletee cancellare la innerHTMLmemoria non cancella completamente. Purtroppo non è un bug ad alta priorità.
Chris Broadfoot

2

Dato che Google non fornisce gunload () per api v3, è meglio utilizzare iframe in html e assegnare map.html come sorgente a questo iframe. dopo l'uso rendere src come null. Ciò libererà sicuramente la memoria consumata dalla mappa.


2
Ogni istanza dell'iframe dovrebbe quindi ricaricare l'API delle mappe, il che non è l'ideale.
Chad Killingsworth

1

Quando rimuovi il div, rimuove il pannello di visualizzazione e la mappa scomparirà. Per rimuovere l'istanza della mappa, assicurati che il tuo riferimento alla mappa sia impostato su nulle che tutti i riferimenti ad altre parti della mappa siano impostati su null. A quel punto, la garbage collection di JavaScript si occuperà della pulizia, come descritto in: Come funziona la garbage collection in JavaScript? .


1
Non sono sicuro che l'impostazione della variabile map su null rimuoverà correttamente tutti i listener di eventi.
Chad Killingsworth

1
Non è solo la mappa che deve essere impostata null, ma qualsiasi riferimento a qualsiasi altra cosa. Quindi, se il riferimento al marker è impostato su null, rendendolo irraggiungibile , non c'è modo di raggiungere l'ascoltatore di eventi. Potrebbe essere ancora connesso alla mappa, ma la mappa non può essere raggiunta, quindi è solo un grosso pezzo di memoria che è essenzialmente diventato orfano. È lo stesso che impostare un Array.length = 0; se non ci sono altri riferimenti ai membri, formano semplicemente un gruppo di memoria orfana che è idonea per la garbage collection.
Sean Mickey

0

Immagino tu stia parlando addEventListener. Quando rimuovi gli elementi DOM, alcuni browser perdono questi eventi e non li rimuovono. Questo è il motivo per cui jQuery fa diverse cose quando rimuove un elemento:

  • Rimuove gli eventi quando può usare removeEventListener. Ciò significa che mantiene un array con i listener di eventi aggiunti a questo elemento.
  • Si elimina gli attributi sugli eventi ( onclick, onblur, ecc) utilizzando deletesull'elemento DOM quando addEventListenernon è disponibile (ancora, ha un array in cui memorizza gli eventi aggiunti).
  • Imposta l'elemento per nullevitare perdite di memoria di IE 6/7/8.
  • Quindi rimuove l'elemento.

Mi riferisco principalmente agli eventi interni dell'API di Google Maps. Possono essere aggiunti / rimossi / attivati ​​utilizzando i metodi dell'evento API documentati su developers.google.com/maps/documentation/javascript/… . Sebbene simili nella funzionalità al browser addEventListener, ci sono un gran numero di eventi personalizzati specifici per la mappa (come "bounds_changed" e alcuni di questi gestori di eventi si agganciano agli eventi del browser come l'evento "resize" della mappa.
Chad Killingsworth

Quindi mantieni una serie di eventi aggiunti e rimuovili quindi manualmente utilizzando removeEventListenero in deletebase al tipo di evento.
Florian Margaine
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.