Qual é a maneira correta de destruir uma instância do mapa?

92

Recentemente, desenvolvi um aplicativo móvel html5. O aplicativo era uma página única em que os eventos de mudança de hash de navegação substituíram todo o DOM. Uma seção do aplicativo era um Google Map usando API v3. Antes que o div do mapa seja removido do DOM, quero remover quaisquer manipuladores / ouvintes de eventos e liberar o máximo de memória possível, pois o usuário não pode retornar a essa seção novamente.

Qual é a melhor maneira de destruir uma instância do mapa?

Chad Killingsworth
fonte
Pergunta relacionada (2014): stackoverflow.com/questions/21142483/…
kashiraja
Código para tentar remover todos os ouvintes de evento em um mapa, bug do Google Maps 35821412
kashiraja

Respostas:

49

Estou adicionando uma segunda resposta a essa pergunta, porque não quero remover as idas e vindas que recebemos por meio de comentários de acompanhamento em minha resposta anterior.

Porém, recentemente me deparei com algumas informações que abordam diretamente sua pergunta e, portanto, gostaria de compartilhar. Não sei se você está ciente disso, mas durante o vídeo do horário comercial da API do Google Maps em 9 de maio de 2012 , Chris Broadfoot e Luke Mahe, do Google, discutiram essa mesma questão do stackoverflow. Se você definir a reprodução do vídeo para 12:50, essa é a seção onde eles discutem sua pergunta.

Essencialmente, eles admitem que é um bug, mas também acrescentam que não suportam casos de uso que envolvam a criação / destruição de instâncias de mapas sucessivas. Eles recomendam fortemente a criação de uma única instância do mapa e a reutilização em qualquer cenário desse tipo. Eles também falam sobre como definir o mapa como nulo e remover explicitamente ouvintes de eventos. Você expressou preocupação sobre os ouvintes de eventos, pensei que apenas definir o mapa como nulo seria suficiente, mas parece que suas preocupações são válidas, porque eles mencionam especificamente os ouvintes de eventos. Eles também recomendaram a remoção completa do DIV que contém o mapa.

De qualquer forma, só queria passar isso adiante e garantir que seja incluído na discussão stackoverflow e espero que ajude você e outros-

Sean Mickey
fonte
2
Obrigado - pedi que respondessem à questão no horário comercial, mas ainda não tive a chance de verificar o vídeo.
Chad Killingsworth
Bem, você poderia simplesmente ter atualizado a resposta anterior mencionando que é uma atualização ...
TJ
4
Bem incrível .. é 2018 e ainda não parece haver uma maneira de fazer isso.
JP Silvashy
28

A resposta oficial é não. As instâncias de mapa em um aplicativo de página única devem ser reutilizadas, não destruídas e então recriadas.

Para alguns aplicativos de página única, isso pode significar uma nova arquitetura da solução de forma que, uma vez que um mapa seja criado, ele possa ser escondido ou desconectado do DOM, mas nunca é destruído / recriado.

Chad Killingsworth
fonte
Isso é muito, muito ruim - eu tenho um aplicativo de página única multilíngue e quero exibir o Google Map no idioma selecionado.
artuska
Parece que isso foi corrigido na versão 3.38.1 (embora eu não tenha verificado de forma independente).
Natal de
15

Visto que, aparentemente, você não pode realmente destruir as instâncias do mapa, uma maneira de reduzir este problema se

  • você precisa mostrar vários mapas de uma vez em um site
  • o número de mapas pode mudar com a interação do usuário
  • os mapas precisam ser ocultados e mostrados novamente junto com outros componentes (ou seja, eles não aparecem em uma posição fixa no DOM)

está mantendo um pool de instâncias do mapa. O pool rastreia as instâncias em uso e, quando é solicitada uma nova instância, verifica se alguma das instâncias do mapa disponível está livre: se estiver, retornará uma existente; se não estiver, criará um nova instância do mapa e retorná-la, adicionando-a ao pool. Dessa forma, você terá apenas um número máximo de instâncias igual ao número máximo de mapas que você já mostra simultaneamente na tela. Estou usando este código (requer 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
    }
 }
}

Você passa as opções do mapa inicial (de acordo com o segundo argumento do construtor google.maps.Map) e ele retorna a instância do mapa (na qual você pode chamar funções pertencentes a google.maps.Map) e o contêiner, que você pode estilizar usando a classe "myDivClassHereForStyling" e pode anexar dinamicamente ao DOM. Se você precisar reiniciar o sistema, pode usar mapInstancesPool.reset (). Ele redefinirá o contador para 0, enquanto mantém todas as instâncias existentes no pool para reutilização. Em meu aplicativo, precisei remover todos os mapas de uma vez e criar um novo conjunto de mapas, então não há função para reciclar uma instância específica do mapa: sua milhagem pode variar. Para remover os mapas da tela, eu uso o detach do jQuery, que não destrói o contêiner do mapa.

Usando este sistema, e usando

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

e correndo

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

(onde divReference é o objeto jQuery do div retornado do pool de instâncias) em cada div que estou removendo, consegui manter o uso de memória do Chrome mais ou menos estável, em vez de aumentar toda vez que excluo mapas e adiciono novos.

Paolo Mioni
fonte
VOCÊ SALVOU MINHA VIDA! Thnks;)
Felipe Desiderati
Feliz por ajudar!!
Paolo Mioni
5

Eu teria sugerido remover o conteúdo do div do mapa e usá-lo deletena variável que contém a referência ao mapa e, provavelmente, explicitar deletequalquer ouvinte de evento.

Porém, há um bug reconhecido e isso pode não funcionar.

Andrew Leach
fonte
Esta é uma boa discussão. Não acho que chamar deleteacrescente muito (consulte stackoverflow.com/q/742623/1314132 ), mas realmente não faz mal. No final, tudo se resume a esta questão: existem referências ao objeto? Se sim, não haverá coleta de lixo.
Sean Mickey
1
@SeanMickey: é onde o bug se torna relevante. A versão 2 deve GUnload()remover todas as referências internas da API.
Andrew Leach
Eu tenho testado com esta página no Chrome: people.missouristate.edu/chadkillingsworth/mapsexamples/… Até agora, o uso de memória após o mapa ser removido cai apenas ligeiramente, mas não está nem perto do nível antes de o mapa ser instanciado.
Chad Killingsworth
@AndrewLeach Com certeza. Mas se eles tiverem um bug que está causando um vazamento de memória, não há muito que possamos fazer até que seja corrigido. Quero dizer, se tornar todos os objetos do mapa inacessíveis não funciona, então deletenão é realmente um conserto. Eles têm que consertar o grande para que tornar as referências inacessíveis funcione como deveria ou adicionar uma nova função que forneça a funcionalidade que você descreveu GUnload().
Sean Mickey
1
Chad / Andrew: sim, eu reproduzi esse problema, infelizmente, deletee limpar a innerHTMLmemória não limpa completamente. Infelizmente, não é um bug de alta prioridade.
Chris Broadfoot
2

Como o google não fornece gunload () para api v3, é melhor usar iframe em html e atribuir map.html como uma fonte para este iframe. após o uso, torne o src nulo. Isso definitivamente irá liberar a memória consumida pelo mapa.

Kumar
fonte
2
Cada instância do iframe teria então que recarregar a API de mapas, o que não é ideal.
Chad Killingsworth de
1

Quando você remove o div, o painel de exibição é removido e o mapa desaparecerá. Para remover a instância do mapa, apenas certifique-se de que sua referência ao mapa seja definida como nulle que quaisquer referências a outras partes do mapa sejam definidas como null. Nesse ponto, a coleta de lixo do JavaScript cuidará da limpeza, conforme descrito em: Como funciona a coleta de lixo no JavaScript? .

Sean Mickey
fonte
1
Não tenho certeza de que definir a variável do mapa como nulo removerá corretamente todos os ouvintes de eventos.
Chad Killingsworth
1
Não é apenas o mapa que deve ser definido null, mas qualquer referência a qualquer outra coisa. Portanto, se a referência do marcador for definida como null, tornando-o inacessível , não há como acessar o ouvinte do evento. Ele ainda pode estar conectado ao mapa, mas o mapa não pode ser alcançado, então é apenas um grande pedaço de memória que se tornou essencialmente órfão. É o mesmo que definir um Array.length = 0; se não houver outras referências aos membros, eles apenas formarão um grupo de memória órfã que é elegível para a coleta de lixo.
Sean Mickey
0

Acho que você está falando addEventListener. Quando você remove os elementos DOM, alguns navegadores vazam esses eventos e não os remove. É por isso que o jQuery faz várias coisas ao remover um elemento:

  • Ele remove os eventos quando pode usar removeEventListener. Isso significa que ele está mantendo uma matriz com os ouvintes de evento adicionados a este elemento.
  • Elimina os atributos sobre eventos ( onclick, onblur, etc), utilizando deleteno elemento DOM quando addEventListenernão está disponível (ainda, que tem uma matriz onde ele armazena os eventos adicionados).
  • Ele define o elemento nullpara evitar vazamentos de memória do IE 6/7/8.
  • Em seguida, remove o elemento.
Florian Margaine
fonte
Estou me referindo principalmente aos eventos internos da API do Google Maps. Eles podem ser adicionados / removidos / acionados usando os métodos de evento API documentados em developers.google.com/maps/documentation/javascript/… . Embora semelhante em funcionalidade ao addEventListener do navegador, há um grande número de eventos personalizados específicos para o mapa (como "bounds_changed" e alguns desses manipuladores de eventos se conectam aos eventos do navegador, como o evento "redimensionar" do mapa.
Chad Killingsworth
Em seguida, mantenha uma matriz dos eventos adicionados e remova-os manualmente usando removeEventListenerou deletedependendo do tipo de evento.
Florian Margaine