Jaki jest właściwy sposób niszczenia instancji mapy?

89

Niedawno opracowałem aplikację mobilną html5. Aplikacja była pojedynczą stroną, na której zdarzenia zmiany skrótu nawigacji zastępowały cały DOM. Jedną z sekcji aplikacji była mapa Google wykorzystująca API v3. Zanim element div mapy zostanie usunięty z DOM, chcę usunąć wszystkie programy obsługi / nasłuchiwania zdarzeń i zwolnić jak najwięcej pamięci, ponieważ użytkownik może nie wrócić do tej sekcji ponownie.

Jaki jest najlepszy sposób na zniszczenie instancji mapy?

Chad Killingsworth
źródło
Powiązane pytanie (2014): stackoverflow.com/questions/21142483/…
kashiraja
Kod do próby usunięcia wszystkich nasłuchujących wydarzeń na mapie, błąd map Google 35821412
Kashiraja

Odpowiedzi:

48

Dodaję drugą odpowiedź na to pytanie, ponieważ nie chcę usuwać tam iz powrotem, które otrzymaliśmy za pośrednictwem dalszych komentarzy do mojej poprzedniej odpowiedzi.

Ale ostatnio natknąłem się na informacje, które bezpośrednio odnoszą się do twojego pytania, więc chciałem się nimi podzielić. Nie wiem, czy zdajesz sobie z tego sprawę, ale podczas godzin pracy w Google Maps API API 9 maja 2012 Video , Chris Broadfoot i Luke Mahe z Google omówili to właśnie pytanie ze stackoverflow. Jeśli ustawisz odtwarzanie wideo na 12:50, to jest sekcja, w której omawiają twoje pytanie.

Zasadniczo przyznają, że jest to błąd, ale dodają również, że tak naprawdę nie obsługują przypadków użycia, które obejmują tworzenie / niszczenie kolejnych instancji mapy. Zdecydowanie zalecają utworzenie jednej instancji mapy i ponowne użycie jej w dowolnym tego rodzaju scenariuszu. Mówią również o ustawieniu mapy na null i jawnym usunięciu detektorów zdarzeń. Wyraziłeś obawy dotyczące detektorów zdarzeń, pomyślałem, że wystarczy ustawić mapę na wartość null, ale wygląda na to, że Twoje obawy są uzasadnione, ponieważ wspominają konkretnie o odbiornikach zdarzeń. Zalecili również całkowite usunięcie DIV, który również trzyma mapę.

W każdym razie chciałem to przekazać i upewnić się, że jest to uwzględnione w dyskusji o przepływie stosu i mam nadzieję, że pomoże to Tobie i innym-

Sean Mickey
źródło
2
Dzięki - poprosiłem ich, aby odpowiedzieli na to pytanie w godzinach pracy, ale nie miałem jeszcze okazji sprawdzić wideo.
Chad Killingsworth
Mogłeś po prostu zaktualizować poprzednią odpowiedź, wspominając, że jest to aktualizacja ...
TJ
4
Cóż, super… jest 2018 rok i wciąż nie ma sposobu, aby to zrobić.
JP Silvashy
28

Oficjalna odpowiedź jest nie. Instancje mapowania w aplikacji jednostronicowej powinny być ponownie używane, a nie niszczone, a następnie odtwarzane.

W przypadku niektórych aplikacji jednostronicowych może to oznaczać zmianę architektury rozwiązania w taki sposób, że po utworzeniu mapy może zostać ukryta lub odłączona od modelu DOM, ale nigdy nie zostanie zniszczona / odtworzona.

Chad Killingsworth
źródło
To bardzo, bardzo źle - mam wielojęzyczną aplikację jednostronicową i chcę wyświetlić Google Map w wybranym języku.
artuska
14

Ponieważ najwyraźniej nie można naprawdę zniszczyć instancji mapy, sposobem na zmniejszenie tego problemu jest, jeśli

  • musisz wyświetlić kilka map jednocześnie na stronie internetowej
  • liczba map może się zmieniać w zależności od interakcji użytkownika
  • mapy muszą być ukryte i ponownie pokazane razem z innymi komponentami (tj. nie pojawiają się w ustalonej pozycji w DOM)

zachowuje pulę instancji map. Pula śledzi używane instancje, a gdy zażąda nowej instancji, sprawdza, czy któraś z dostępnych instancji mapy jest wolna: jeśli tak, zwróci istniejącą, jeśli nie, utworzy nową instancję mapy i zwróć ją, dodając ją do puli. W ten sposób będziesz mieć tylko maksymalną liczbę instancji równą maksymalnej liczbie map, które kiedykolwiek pokażesz jednocześnie na ekranie. Używam tego kodu (wymaga 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
    }
 }
}

Przekazujesz mu początkowe opcje mapy (zgodnie z drugim argumentem konstruktora google.maps.Map) i zwraca on zarówno instancję mapy (na której można wywoływać funkcje dotyczące google.maps.Map), jak i kontener, który możesz stylizować za pomocą klasy „myDivClassHereForStyling” i możesz dynamicznie dołączać do DOM. Jeśli chcesz zresetować system, możesz użyć mapInstancesPool.reset (). Zresetuje licznik do 0, zachowując wszystkie istniejące instancje w puli do ponownego wykorzystania. W mojej aplikacji musiałem usunąć wszystkie mapy naraz i utworzyć nowy zestaw map, więc nie ma funkcji recyklingu określonej instancji mapy: Twój przebieg może się różnić. Aby usunąć mapy z ekranu, używam detach jQuery, które nie niszczą kontenera mapy.

Korzystając z tego systemu i używając

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

i bieganie

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

(gdzie divReference to obiekt jQuery div zwrócony z puli instancji) na każdym usuwanym elemencie div udało mi się utrzymać mniej więcej stabilne użycie pamięci Chrome, w przeciwieństwie do zwiększania się za każdym razem, gdy usuwam mapy i dodawałem nowe.

Paolo Mioni
źródło
OCALIŚCIE MOJE ŻYCIE! Dzięki;)
Felipe Desiderati
Cieszę się, że mogłem pomóc!
Paolo Mioni
5

Sugerowałbym usunięcie zawartości div mapy i użycie deletena zmiennej przechowującej odniesienie do mapy i prawdopodobnie jawnie deletejakichkolwiek detektorów zdarzeń.

Istnieje jednak potwierdzony błąd , który może nie działać.

Andrew Leach
źródło
To dobra dyskusja. Nie sądzę, aby dzwonienie deletewiele dodało (patrz stackoverflow.com/q/742623/1314132 ), ale naprawdę nie zaszkodzi. W końcu sprowadza się do pytania: czy są jakieś odniesienia do obiektu? Jeśli tak, nie będzie to śmieci.
Sean Mickey
1
@SeanMickey: w którym miejscu błąd staje się istotny. Wersja 2 musi GUnload()usunąć wszystkie wewnętrzne odwołania API.
Andrew Leach
Testowałem na tej stronie w Chrome: people.missouristate.edu/chadkillingsworth/mapsexamples/… Jak dotąd użycie pamięci po usunięciu mapy spada tylko nieznacznie, ale nie jest w pobliżu poziomu przed utworzeniem mapy.
Chad Killingsworth
@AndrewLeach Absolutely. Ale jeśli mają błąd powodujący wyciek pamięci, niewiele możemy zrobić, dopóki nie zostanie naprawiony. Chodzi mi o to, że jeśli uczynienie wszystkich obiektów mapy nieosiągalnymi nie działa, deleteto tak naprawdę nie jest poprawką. Muszą naprawić duże, aby uczynić odniesienia nieosiągalnymi działaniami tak, jak powinno, lub dodać nową funkcję, która zapewnia funkcjonalność, którą opisujesz GUnload().
Sean Mickey
1
Chad / Andrew: tak, odtworzyłem ten problem, niestety deletei wyczyszczenie innerHTMLnie wyczyści całkowicie pamięci. Niestety nie jest to błąd o wysokim priorytecie.
Chris Broadfoot
2

Ponieważ Google nie udostępnia narzędzia gunload () dla interfejsu API v3, lepiej użyj elementu iframe w html i przypisz map.html jako źródło do tego elementu iframe. po użyciu ustaw src na null. To na pewno zwolni pamięć zajmowaną przez mapę.

kumar
źródło
2
Każda instancja elementu iframe musiałaby wówczas ponownie załadować interfejs API map, co nie jest idealne.
Chad Killingsworth,
1

Usunięcie divikony spowoduje usunięcie panelu wyświetlania i mapa zniknie. Aby usunąć instancję mapy, po prostu upewnij się, że odniesienie do mapy jest ustawione na, nulla wszelkie odniesienia do innych części mapy są ustawione na null. W tym momencie wyrzucanie elementów bezużytecznych JavaScript zajmie się czyszczeniem, jak opisano w: Jak działa czyszczenie pamięci w JavaScript? .

Sean Mickey
źródło
1
Nie jestem pewien, czy ustawienie zmiennej mapy na wartość null spowoduje prawidłowe usunięcie wszystkich detektorów zdarzeń.
Chad Killingsworth
1
To nie tylko mapa, którą należy ustawić null, ale wszelkie odniesienia do czegokolwiek innego. Więc jeśli odniesienie do znacznika jest ustawione na null, co czyni go nieosiągalnym , nie ma sposobu, aby dotrzeć do detektora zdarzeń. Może nadal być połączony z mapą, ale mapa nie jest dostępna, więc jest to po prostu duża porcja pamięci, która została w zasadzie osierocona. To to samo, co ustawienie Array.length = 0; jeśli nie ma innych odniesień do członków, po prostu tworzą grupę osieroconej pamięci, która kwalifikuje się do czyszczenia pamięci.
Sean Mickey
0

Chyba o tym mówisz addEventListener. Kiedy usuwasz elementy DOM, niektóre przeglądarki przeciekają te zdarzenia i ich nie usuwają. Dlatego jQuery robi kilka rzeczy podczas usuwania elementu:

  • Usuwa zdarzenia, gdy może używać removeEventListener. Oznacza to, że przechowuje tablicę z detektorami zdarzeń, które dodała do tego elementu.
  • Usuwa atrybuty o zdarzeniach ( onclick, onbluritp), stosując deletena elemencie DOM, gdy addEventListenernie jest dostępna (jeszcze, że ma tablicę w której przechowywane są zdarzenia własne).
  • Ustawia element tak, nullaby uniknąć wycieków pamięci IE 6/7/8.
  • Następnie usuwa element.
Florian Margaine
źródło
Mam na myśli głównie wewnętrzne zdarzenia Google Maps API. Można je dodawać / usuwać / wyzwalać za pomocą metod zdarzeń interfejsu API udokumentowanych pod adresem developers.google.com/maps/documentation/javascript/… . Funkcjonalność podobna do addEventListener przeglądarki, ale istnieje duża liczba niestandardowych zdarzeń specyficznych dla mapy (takich jak „bounds_changed”, a niektóre z tych programów obsługi zdarzeń łączą się ze zdarzeniami przeglądarki, takimi jak zdarzenie „resize” mapy.
Chad Killingsworth
Następnie zachowaj tablicę dodanych zdarzeń i usuń, a następnie ręcznie za pomocą removeEventListenerlub w deletezależności od rodzaju zdarzenia.
Florian Margaine