Как правильно уничтожить экземпляр карты?

89

Недавно я разработал мобильное приложение html5. Приложение представляло собой единую страницу, на которой события изменения хеш-функции навигации заменяли всю DOM. Одним из разделов приложения была карта Google с использованием API v3. Перед тем, как div карты будет удален из DOM, я хочу удалить все обработчики / прослушиватели событий и освободить как можно больше памяти, поскольку пользователь не может снова вернуться в этот раздел.

Как лучше всего уничтожить экземпляр карты?

Чад Киллингсворт
источник
Связанный вопрос (2014): stackoverflow.com/questions/21142483/…
kashiraja 07
Код для попытки удалить все прослушиватели событий на карте, ошибка карт Google 35821412
kashiraja

Ответы:

48

Я добавляю второй ответ на этот вопрос, потому что я не хочу убирать то, что у нас было в последующих комментариях к моему предыдущему ответу.

Но недавно я наткнулся на некоторую информацию, которая напрямую касается вашего вопроса, и поэтому я хотел поделиться. Я не знаю, знаете ли вы об этом, но во время рабочего дня Google Maps API Office Hours 9 мая 2012 г. Крис Бродфут и Люк Маэ из Google обсуждали именно этот вопрос из stackoverflow. Если вы установите воспроизведение видео на 12:50, это раздел, в котором они обсуждают ваш вопрос.

По сути, они признают, что это ошибка, но также добавляют, что на самом деле они не поддерживают варианты использования, которые включают создание / уничтожение последовательных экземпляров карты. Они настоятельно рекомендуют создать единственный экземпляр карты и повторно использовать его в любом подобном сценарии. Они также говорят об установке для карты значения NULL и явном удалении прослушивателей событий. Вы выразили обеспокоенность по поводу прослушивателей событий, я подумал, что достаточно просто установить для карты значение null, но похоже, что ваши опасения обоснованы, потому что они конкретно упоминают прослушиватели событий. Они также рекомендовали полностью удалить DIV, который также содержит карту.

В любом случае, просто хотел передать это и убедиться, что он включен в обсуждение stackoverflow, и надеюсь, что это поможет вам и другим -

Шон Микки
источник
2
Спасибо - я попросил их ответить на вопрос в рабочее время, но у меня еще не было возможности посмотреть видео.
Чад Киллингсворт
Что ж, вы могли бы просто обновить предыдущий ответ, упомянув, что это обновление ...
TJ
4
Что ж, круто ... Сейчас 2018 год, а способа сделать это, похоже, пока нет.
JP Silvashy
28

Официальный ответ является не нужно. Экземпляры карт в одностраничном приложении следует использовать повторно, а не уничтожать, а затем воссоздавать.

Для некоторых одностраничных приложений это может означать изменение архитектуры решения таким образом, чтобы после создания карты она могла быть скрыта или отключена от DOM, но никогда не уничтожалась / не воссоздавалась.

Чад Киллингсворт
источник
Это очень плохо - у меня есть многоязычное одностраничное приложение, и я хочу отображать Google Map на выбранном языке.
artuska
14

Поскольку очевидно, что вы не можете действительно уничтожить экземпляры карты, способ уменьшить эту проблему, если

  • нужно показать на сайте сразу несколько карт
  • количество карт может измениться при взаимодействии с пользователем
  • карты должны быть скрыты и повторно показаны вместе с другими компонентами (т.е. они не отображаются в фиксированной позиции в DOM)

хранит пул экземпляров карты. Пул отслеживает используемые экземпляры, и когда он запрашивает новый экземпляр, он проверяет, свободен ли какой-либо из доступных экземпляров карты: если это так, он вернет существующий, если нет, он создаст новый экземпляр карты и верните его, добавив в пул. Таким образом, у вас будет только максимальное количество экземпляров, равное максимальному количеству карт, которые вы когда-либо одновременно отображали на экране. Я использую этот код (требуется 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
    }
 }
}

Вы передаете ему начальные параметры карты (согласно второму аргументу конструктора google.maps.Map), и он возвращает как экземпляр карты (на котором вы можете вызывать функции, относящиеся к google.maps.Map), так и контейнер, который вы можете стилизовать, используя класс «myDivClassHereForStyling», и вы можете динамически присоединяться к DOM. Если вам нужно перезагрузить систему, вы можете использовать mapInstancesPool.reset (). Он сбросит счетчик на 0, сохраняя при этом все существующие экземпляры в пуле для повторного использования. В моем приложении мне нужно было удалить все карты сразу и создать новый набор карт, поэтому нет функции для переработки конкретного экземпляра карты: ваш пробег может отличаться. Чтобы удалить карты с экрана, я использую функцию отсоединения jQuery, которая не разрушает контейнер карты.

Используя эту систему и используя

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

и бег

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

(где divReference - это объект jQuery div, возвращаемый из пула экземпляров) на каждом удаляемом div мне удалось сохранить более или менее стабильное использование памяти Chrome, в отличие от его увеличения каждый раз, когда я удаляю карты и добавляю новые.

Паоло Миони
источник
ВЫ СПАСИЛИ МОЮ ЖИЗНЬ! Спасибо;)
Фелипе Дезидерати
Рад помочь!!
Паоло Миони,
5

Я бы предложил удалить содержимое div карты и использовать deleteпеременную, содержащую ссылку на карту, и, возможно, явно deleteуказать какие-либо прослушиватели событий.

Однако есть признанная ошибка , и это может не сработать.

Эндрю Лич
источник
Это хорошее обсуждение. Я не думаю, что вызов deleteдобавляет много (см. Stackoverflow.com/q/742623/1314132 ), но это действительно не повредит. В конце концов, все сводится к вопросу: есть ли ссылки на объект? Если да, сборщиком мусора не будет.
Шон Микки
1
@SeanMickey: именно здесь ошибка становится актуальной. Версия 2 GUnload()должна удалить все внутренние ссылки API.
Эндрю Лич
Я тестировал эту страницу в Chrome: people.missouristate.edu/chadkillingsworth/mapsexamples/… Пока что использование памяти после удаления карты снижается незначительно, но далеко не до уровня до создания экземпляра карты.
Чад Киллингсворт,
@AndrewLeach Совершенно верно. Но если у них есть ошибка, вызывающая утечку памяти, мы мало что можем сделать, пока она не будет исправлена. Я имею в виду, что если сделать все объекты карты недоступными не получается, то deleteэто не совсем исправление. Они должны исправить большую проблему, чтобы сделать ссылки недоступными, работали должным образом, или добавить новую функцию, обеспечивающую описанные вами функции GUnload().
Шон Микки
1
Чад / Эндрю: да, я воспроизвел эту проблему, к сожалению, deleteи очистка innerHTMLне полностью очищает память. К сожалению, это не высокоприоритетная ошибка.
Крис Бродфут,
2

Поскольку Google не предоставляет gunload () для api v3, лучше используйте iframe в html и назначьте map.html в качестве источника для этого iframe. после использования сделайте src нулевым. Это определенно освободит память, потребляемую картой.

кумар
источник
2
Затем каждый экземпляр iframe должен будет перезагружать api карт, что не идеально.
Чад Киллингсворт,
1

Когда вы удалите div, это приведет к удалению панели дисплея, и карта исчезнет. Чтобы удалить экземпляр карты, просто убедитесь, что для вашей ссылки на карту установлено значение nullи что для всех ссылок на другие части карты установлено значение null. В этот момент сборка мусора JavaScript позаботится об очистке, как описано в разделе : Как сборка мусора работает в JavaScript? .

Шон Микки
источник
1
Я не уверен, что установка для переменной карты значения null должным образом удалит всех прослушивателей событий.
Чад Киллингсворт
1
Необходимо установить не только карту null, но и любые ссылки на что-либо еще. Таким образом, если ссылка на маркер установлена ​​в значение null, что делает его недоступным , нет возможности связаться с прослушивателем событий. Он все еще может быть подключен к карте, но карта недоступна, поэтому это просто большой кусок памяти, который по существу стал бесхозным. Это то же самое, что и установка Array.length = 0; если нет других ссылок на элементы, они просто образуют группу осиротевшей памяти, подходящую для сборки мусора.
Шон Микки
0

Я предполагаю, что вы говорите addEventListener. Когда вы удаляете элементы DOM, некоторые браузеры пропускают эти события и не удаляют их. Вот почему jQuery при удалении элемента выполняет несколько действий:

  • Он удаляет события, когда может использовать removeEventListener. Это означает, что он хранит массив со слушателями событий, которые он добавил в этот элемент.
  • Он удаляет атрибуты о событиях ( onclick, onblurи т. Д.), Которые используются deleteв элементе DOM, когда addEventListenerон недоступен (тем не менее, у него есть массив, в котором он хранит добавленные события).
  • Он устанавливает элемент, чтобы nullизбежать утечек памяти IE 6/7/8.
  • Затем он удаляет элемент.
Флориан Маргейн
источник
В основном я имею в виду внутренние события API Карт Google. Их можно добавлять / удалять / запускать с помощью методов событий API, задокументированных на странице developers.google.com/maps/documentation/javascript/… . Хотя функциональность аналогична браузеру addEventListener, существует большое количество настраиваемых событий, специфичных для карты (таких как «bounds_changed», и некоторые из этих обработчиков событий подключаются к событиям браузера, таким как событие «изменения размера» карты.
Чад Киллингсворт)
Затем сохраните массив добавленных событий и удалите их вручную, используя removeEventListenerили в deleteзависимости от типа события.
Флориан Маргейн