Quelle est la bonne façon de détruire une instance de carte?

89

J'ai récemment développé une application mobile html5. L'application était une page unique où les événements de changement de hachage de navigation remplaçaient l'ensemble du DOM. Une section de l'application était une carte Google utilisant l'API v3. Avant que la map div ne soit supprimée du DOM, je souhaite supprimer tous les gestionnaires / écouteurs d'événements et libérer autant de mémoire que possible car l'utilisateur ne peut pas revenir à cette section.

Quelle est la meilleure façon de détruire une instance de carte?

Chad Killingsworth
la source
Question connexe (2014): stackoverflow.com/questions/21142483/…
kashiraja
Code pour tenter de supprimer tous les écouteurs d'événements sur une carte, bogue Google Maps 35821412
kashiraja

Réponses:

48

J'ajoute une deuxième réponse à cette question, car je ne veux pas supprimer le va-et-vient que nous avons eu via des commentaires de suivi sur ma réponse précédente.

Mais je suis récemment tombé sur des informations qui répondent directement à votre question et je voulais donc les partager. Je ne sais pas si vous en êtes conscient, mais lors de la vidéo du 9 mai 2012 sur l'API Google Maps , Chris Broadfoot et Luke Mahe de Google ont discuté de cette question de stackoverflow. Si vous réglez la lecture vidéo sur 12h50, c'est la section où ils discutent de votre question.

Essentiellement, ils admettent qu'il s'agit d'un bogue, mais ajoutent également qu'ils ne supportent pas vraiment les cas d'utilisation qui impliquent la création / destruction d'instances de carte successives. Ils recommandent fortement de créer une seule instance de la carte et de la réutiliser dans n'importe quel scénario de ce type. Ils parlent également de définir la carte sur null et de supprimer explicitement les écouteurs d'événements. Vous avez exprimé des inquiétudes à propos des écouteurs d'événements, je pensais que définir la carte sur null suffirait, mais il semble que vos préoccupations soient valables, car elles mentionnent spécifiquement les écouteurs d'événements. Ils ont également recommandé de supprimer complètement le DIV qui contient la carte.

Quoi qu'il en soit, je voulais juste le transmettre et vous assurer qu'il est inclus dans la discussion de stackoverflow et j'espère que cela vous aidera, vous et les autres.

Sean Mickey
la source
2
Merci - Je leur ai demandé de répondre à la question aux heures de bureau, mais je n'avais pas encore eu l'occasion de regarder la vidéo.
Chad Killingsworth
Eh bien, vous auriez pu simplement mettre à jour la réponse précédente en mentionnant qu'il s'agit d'une mise à jour ...
TJ
4
Bien génial ... nous sommes en 2018 et il ne semble toujours pas y avoir de moyen de le faire.
JP Silvashy
28

La réponse officielle est que non. Les instances de mappage dans une application à page unique doivent être réutilisées et non détruites puis recréées.

Pour certaines applications à page unique, cela peut signifier une nouvelle architecture de la solution de telle sorte qu'une fois qu'une carte est créée, elle peut être cachée ou déconnectée du DOM, mais elle n'est jamais détruite / recréée.

Chad Killingsworth
la source
C'est très très mauvais - j'ai une application de page unique multilingue et je veux afficher Google Map sur la langue sélectionnée.
artuska
14

Comme apparemment, vous ne pouvez pas vraiment détruire les instances de carte, un moyen de réduire ce problème si

  • vous devez afficher plusieurs cartes à la fois sur un site Web
  • le nombre de cartes peut changer avec l'interaction de l'utilisateur
  • les cartes doivent être masquées et ré-affichées avec d'autres composants (c'est-à-dire qu'elles n'apparaissent pas dans une position fixe dans le DOM)

conserve un pool d'instances de carte. Le pool garde la trace des instances utilisées, et quand il est demandé une nouvelle instance, il vérifie si l'une des instances de carte disponibles est libre: si c'est le cas, il en retournera une existante, si ce n'est pas le cas, il créera un nouvelle instance de carte et la renvoyer, en l'ajoutant au pool. De cette façon, vous n'aurez qu'un nombre maximum d'instances égal au nombre maximum de cartes que vous affichez simultanément à l'écran. J'utilise ce code (il nécessite 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
    }
 }
}

Vous lui transmettez les options de carte de départ (selon le deuxième argument du constructeur de google.maps.Map), et il renvoie à la fois l'instance de la carte (sur laquelle vous pouvez appeler des fonctions relatives à google.maps.Map) et le conteneur, qui vous pouvez styliser en utilisant la classe "myDivClassHereForStyling", et vous pouvez ajouter de façon dynamique au DOM. Si vous devez réinitialiser le système, vous pouvez utiliser mapInstancesPool.reset (). Il réinitialisera le compteur à 0, tout en conservant toutes les instances existantes du pool pour une réutilisation. Dans mon application, j'avais besoin de supprimer toutes les cartes en même temps et de créer un nouvel ensemble de cartes, il n'y a donc pas de fonction pour recycler une instance de carte spécifique: votre kilométrage peut varier. Pour supprimer les cartes de l'écran, j'utilise le détachement de jQuery, qui ne détruit pas le conteneur de la carte.

En utilisant ce système et en utilisant

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

et courir

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

(où divReference est l'objet jQuery de la div renvoyé par le pool d'instances) sur chaque div que je supprime, j'ai réussi à maintenir l'utilisation de la mémoire de Chrome plus ou moins stable, au lieu de l'augmenter chaque fois que je supprime des cartes et en ajoute de nouvelles.

Paolo Mioni
la source
VOUS AVEZ SAUVÉ MA VIE! Thnks;)
Felipe Desiderati
Heureux d'avoir pu aider!!
Paolo Mioni
5

J'aurais suggéré de supprimer le contenu de la carte div et d'utiliser deletela variable contenant la référence à la carte, et probablement explicitement deleteles écouteurs d'événements.

Il y a un bogue reconnu , cependant, et cela peut ne pas fonctionner.

Andrew Leach
la source
C'est une bonne discussion. Je ne pense pas qu'appeler deleteajoute beaucoup (voir stackoverflow.com/q/742623/1314132 ), mais cela ne peut vraiment pas faire de mal. Au final, cela revient à cette question: y a-t-il des références à l'objet? Si oui, il ne sera pas ramassé.
Sean Mickey
1
@SeanMickey: c'est là que le bogue devient pertinent. La version 2 doit GUnload()supprimer toutes les références internes de l'API.
Andrew Leach
J'ai testé cette page dans Chrome: people.missouristate.edu/chadkillingsworth/mapsexamples / ... Jusqu'à présent, l'utilisation de la mémoire après la suppression de la carte ne diminue que légèrement, mais est loin du niveau avant l'instanciation de la carte.
Chad Killingsworth
@AndrewLeach Absolument. Mais s'ils ont un bogue qui cause une fuite de mémoire, nous ne pouvons pas faire grand-chose jusqu'à ce qu'il soit corrigé. Je veux dire, si rendre tous les objets de la carte inaccessibles ne fonctionne pas, alors ce deleten'est pas vraiment une solution. Ils doivent corriger le gros pour que rendre les références inaccessibles fonctionne comme il se doit ou ajouter une nouvelle fonction qui fournit la fonctionnalité que vous décrivez GUnload().
Sean Mickey
1
Chad / Andrew: oui, j'ai reproduit ce numéro, malheureusement deleteet effacer la innerHTMLmémoire n'efface pas complètement la mémoire. Malheureusement, ce n'est pas un bogue hautement prioritaire.
Chris Broadfoot
2

Comme google ne fournit pas gunload () pour l'api v3, mieux vaut utiliser iframe en html et attribuer map.html comme source à cet iframe. après utilisation, rendre src nul. Cela libérera définitivement la mémoire consommée par la carte.

Kumar
la source
2
Chaque instance de l'iframe devrait alors recharger l'API de la carte, ce qui n'est pas idéal.
Chad Killingsworth
1

Lorsque vous supprimez le div, cela supprime le panneau d'affichage et la carte disparaît. Pour supprimer l'instance de carte, assurez-vous simplement que votre référence à la carte est définie sur nullet que toutes les références à d'autres parties de la carte sont définies sur null. À ce stade, le garbage collection JavaScript se chargera du nettoyage, comme décrit dans: Comment le garbage collection fonctionne-t-il en JavaScript? .

Sean Mickey
la source
1
Je ne suis pas sûr que la définition de la variable map sur null supprimera correctement tous les écouteurs d'événements.
Chad Killingsworth
1
Ce n'est pas seulement la carte qui doit être définie null, mais toute référence à autre chose. Donc, si la référence du marqueur est définie sur null, ce qui la rend inaccessible , il n'y a aucun moyen d'atteindre l'écouteur d'événements. Il est peut-être toujours connecté à la carte, mais la carte ne peut pas être atteinte, c'est donc juste un gros morceau de mémoire qui est essentiellement devenu orphelin. C'est la même chose que de définir un Array.length = 0; s'il n'y a pas d'autres références aux membres, ils forment simplement un groupe de mémoire orpheline qui est éligible pour le garbage collection.
Sean Mickey
0

Je suppose que vous parlez addEventListener. Lorsque vous supprimez les éléments DOM, certains navigateurs divulguent ces événements et ne les suppriment pas. C'est pourquoi jQuery fait plusieurs choses lors de la suppression d'un élément:

  • Il supprime les événements lorsqu'il peut utiliser removeEventListener. Cela signifie qu'il garde un tableau avec les écouteurs d'événements qu'il a ajoutés sur cet élément.
  • Il supprime les attributs sur les événements ( onclick, onblur, etc.) à l' aide deletede l'élément DOM lorsqu'il addEventListenerest pas disponible (encore, il a un tableau dans lequel il stocke les événements ajoutés).
  • Il définit l'élément sur nullpour éviter les fuites de mémoire IE 6/7/8.
  • Il supprime ensuite l'élément.
Florian Margaine
la source
Je fais principalement référence aux événements internes de l'API Google Maps. Ils peuvent être ajoutés / supprimés / déclenchés à l'aide des méthodes d'événement API documentées à l' adresse developer.google.com/maps/documentation/javascript/… . Bien que leurs fonctionnalités soient similaires à celles du navigateur addEventListener, il existe un grand nombre d'événements personnalisés spécifiques à la carte (tels que "bounds_changed" et certains de ces gestionnaires d'événements se connectent aux événements du navigateur tels que l'événement "resize" de la carte.
Chad Killingsworth
Ensuite, gardez un tableau des événements ajoutés et supprimez-les puis manuellement en utilisant removeEventListenerou en deletefonction du type d'événement.
Florian Margaine