¿Cuál es la forma correcta de destruir una instancia de mapa?

89

Recientemente desarrollé una aplicación móvil html5. La aplicación era una sola página donde los eventos de cambio de hash de navegación reemplazaban a todo el DOM. Una sección de la aplicación era un mapa de Google usando API v3. Antes de que el mapa div se elimine del DOM, quiero eliminar cualquier controlador / escucha de eventos y liberar tanta memoria como sea posible, ya que el usuario no puede volver a esa sección nuevamente.

¿Cuál es la mejor forma de destruir una instancia de mapa?

Chad Killingsworth
fuente
Pregunta relacionada (2014): stackoverflow.com/questions/21142483/…
kashiraja
Código para intentar eliminar todos los oyentes de eventos en un mapa, error de mapas de Google 35821412
kashiraja

Respuestas:

48

Estoy agregando una segunda respuesta a esta pregunta, porque no quiero eliminar la ida y vuelta que tuvimos a través de los comentarios de seguimiento de mi respuesta anterior.

Pero recientemente encontré información que aborda directamente su pregunta y por eso quería compartirla. No sé si está al tanto de esto, pero durante el video del horario de oficina de la API de Google Maps el 9 de mayo de 2012 , Chris Broadfoot y Luke Mahe de Google discutieron esta misma pregunta de stackoverflow. Si configura la reproducción de video en 12:50, esa es la sección donde discuten su pregunta.

Básicamente, admiten que es un error, pero también añaden que realmente no admiten casos de uso que impliquen la creación / destrucción de instancias de mapas sucesivas. Recomiendan encarecidamente crear una única instancia del mapa y reutilizarla en cualquier escenario de este tipo. También hablan de configurar el mapa en nulo y eliminar explícitamente los detectores de eventos. Expresó su preocupación sobre los oyentes de eventos, pensé que simplemente establecer el mapa en nulo sería suficiente, pero parece que sus preocupaciones son válidas, porque mencionan específicamente a los oyentes de eventos. También recomendaron eliminar por completo el DIV que contiene el mapa.

En cualquier caso, solo quería pasar esto y asegurarme de que esté incluido en la discusión de stackoverflow y espero que te ayude a ti y a otros.

Sean Mickey
fuente
2
Gracias, les pedí que abordaran la pregunta en horario de oficina, pero aún no había tenido la oportunidad de ver el video.
Chad Killingsworth
Bueno, simplemente podría haber actualizado la respuesta anterior mencionando que es una actualización ...
TJ
4
Bueno, genial ... es 2018 y todavía no parece haber una manera de hacer esto.
JP Silvashy
28

La respuesta oficial es que no. Las instancias de mapas en una aplicación de una sola página deben reutilizarse y no destruirse y luego recrearse.

Para algunas aplicaciones de una sola página, esto puede significar rediseñar la solución de modo que una vez que se crea un mapa, se pueda ocultar o desconectar del DOM, pero nunca se destruya / vuelva a crear.

Chad Killingsworth
fuente
Esto es muy, muy malo: tengo una aplicación de una sola página en varios idiomas y quiero mostrar Google Map en el idioma seleccionado.
artuska
14

Dado que aparentemente no puede destruir las instancias del mapa, una forma de reducir este problema si

  • necesitas mostrar varios mapas a la vez en un sitio web
  • la cantidad de mapas puede cambiar con la interacción del usuario
  • los mapas deben ocultarse y volver a mostrarse junto con otros componentes (es decir, no aparecen en una posición fija en el DOM)

mantiene un grupo de instancias de mapas. El grupo realiza un seguimiento de las instancias que se están utilizando y, cuando se solicita una nueva instancia, verifica si alguna de las instancias de mapa disponibles está libre: si lo está, devolverá una existente, si no lo está, creará una nueva instancia de mapa y devuélvala, agregándola al grupo. De esta manera, solo tendrá un número máximo de instancias igual al número máximo de mapas que haya mostrado simultáneamente en la pantalla. Estoy usando este código (requiere 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
    }
 }
}

Le pasa las opciones del mapa de inicio (según el segundo argumento del constructor de google.maps.Map), y devuelve tanto la instancia del mapa (en la que puede llamar a funciones pertenecientes a google.maps.Map) y el contenedor, que puede diseñar usando la clase "myDivClassHereForStyling", y puede agregar dinámicamente al DOM. Si necesita restablecer el sistema, puede usar mapInstancesPool.reset (). Restablecerá el contador a 0, mientras mantiene todas las instancias existentes en el grupo para su reutilización. En mi aplicación, necesitaba eliminar todos los mapas a la vez y crear un nuevo conjunto de mapas, por lo que no hay una función para reciclar una instancia de mapa específica: su kilometraje puede variar. Para eliminar los mapas de la pantalla, utilizo la separación de jQuery, que no destruye el contenedor del mapa.

Al usar este sistema y usar

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

y corriendo

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

(donde divReference es el objeto jQuery del div devuelto del grupo de instancias) en cada div que estoy eliminando, logré mantener el uso de la memoria de Chrome más o menos estable, en lugar de que aumente cada vez que elimino mapas y agrego nuevos.

Paolo Mioni
fuente
¡HAS SALVADO MI VIDA! Gracias;)
Felipe Desiderati
¡¡Me alegra ser de ayuda!!
Paolo Mioni
5

Habría sugerido eliminar el contenido del div del mapa y usarlo deleteen la variable que contiene la referencia al mapa, y probablemente usar explícitamente deletecualquier detector de eventos.

Sin embargo, hay un error reconocido y es posible que esto no funcione.

Andrew Leach
fuente
Esta es una buena discusión. No creo que las llamadas deleteagreguen mucho (consulte stackoverflow.com/q/742623/1314132 ), pero realmente no puede hacer daño. Al final, todo se reduce a esta pregunta: ¿hay alguna referencia al objeto? Si es así, no se recolectará basura.
Sean Mickey
1
@SeanMickey: que es donde el error se vuelve relevante. La versión 2 tiene GUnload()que eliminar todas las referencias internas de la API.
Andrew Leach
He estado probando con esta página en Chrome: people.missouristate.edu/chadkillingsworth/mapsexamples/… Hasta ahora, el uso de memoria después de que se elimina el mapa cae solo ligeramente, pero no se acerca al nivel anterior a la instanciación del mapa.
Chad Killingsworth
@AndrewLeach Absolutamente. Pero si tienen un error que está causando una pérdida de memoria, no hay mucho que podamos hacer hasta que se solucione. Quiero decir, si hacer que todos los objetos del mapa sean inalcanzables no funciona, entonces deleteno es realmente una solución. Tienen que arreglar lo grande para que hacer que las referencias sean inalcanzables funcione como debería o agregar una nueva función que proporcione la funcionalidad que usted describe GUnload().
Sean Mickey
1
Chad / Andrew: sí, he reproducido este problema, desafortunadamente deletey borrar la innerHTMLmemoria no borra completamente la memoria. Desafortunadamente, no es un error de alta prioridad.
Chris Broadfoot
2

Como Google no proporciona gunload () para api v3, es mejor usar iframe en html y asignar map.html como fuente a este iframe. después de su uso, haga que src sea nulo. Eso definitivamente liberará la memoria consumida por el mapa.

Kumar
fuente
2
Cada instancia del iframe tendría que volver a cargar la API de mapas, lo que no es ideal.
Chad Killingsworth
1

Cuando quita el div, se quita el panel de visualización y el mapa desaparecerá. Para eliminar la instancia del mapa, solo asegúrese de que su referencia al mapa esté establecida en nully de que cualquier referencia a otras partes del mapa esté establecida en null. En ese momento, la recolección de basura de JavaScript se encargará de la limpieza, como se describe en: ¿Cómo funciona la recolección de basura en JavaScript? .

Sean Mickey
fuente
1
No estoy seguro de que establecer la variable del mapa en nulo elimine correctamente todos los detectores de eventos.
Chad Killingsworth
1
No es solo el mapa lo que debe configurarse null, sino cualquier referencia a cualquier otra cosa. Entonces, si la referencia del marcador está configurada en null, haciéndola inalcanzable , no hay forma de llegar al detector de eventos. Es posible que aún esté conectado al mapa, pero no se puede acceder al mapa, por lo que es solo un gran trozo de memoria que esencialmente se ha quedado huérfano. Es lo mismo que establecer un Array.length = 0; si no hay otras referencias a los miembros, simplemente forman un grupo de memoria huérfana que es elegible para la recolección de basura.
Sean Mickey
0

Supongo que estás hablando addEventListener. Cuando elimina los elementos DOM, algunos navegadores filtran estos eventos y no los eliminan. Es por eso que jQuery hace varias cosas al eliminar un elemento:

  • Elimina los eventos cuando puede usar removeEventListener. Eso significa que mantiene una matriz con los detectores de eventos que agregó en este elemento.
  • Elimina los atributos sobre los eventos ( onclick, onblur, etc.) utilizando deleteel elemento DOM cuando addEventListenerno está disponible (aún así, tiene una gran variedad donde almacena los eventos añadidos).
  • Establece el elemento para nullevitar pérdidas de memoria de IE 6/7/8.
  • Luego elimina el elemento.
Florian Margaine
fuente
Me refiero principalmente a los eventos internos de la API de Google Maps. Se pueden agregar, eliminar o activar mediante los métodos de eventos de API documentados en developers.google.com/maps/documentation/javascript/… . Si bien tiene una funcionalidad similar a la del navegador addEventListener, hay una gran cantidad de eventos personalizados específicos del mapa (como "limits_changed" y algunos de esos controladores de eventos se conectan a los eventos del navegador, como el evento "resize" del mapa).
Chad Killingsworth
Luego, mantenga una matriz de los eventos agregados y elimínelos manualmente usando removeEventListenero deletedependiendo del tipo de evento.
Florian Margaine