Жизненный цикл карты в Leaflet определяется последовательностью создания, конфигурации, отображения, обновления и полного уничтожения экземпляра карты вместе со всеми связанными слоями, обработчиками событий и DOM-элементами. Корректное управление этим циклом критично для предотвращения утечек памяти, деградации производительности и неконсистентного поведения интерфейса, особенно в SPA-приложениях.
Создание карты запускает формирование внутренней структуры объектов Leaflet: контейнера, системы координат, тайловой проекции и менеджера слоёв.
При вызове:
const map = L.map('map', {
center: [51.505, -0.09],
zoom: 13
});
происходит:
_panes)_events)Ключевым моментом является то, что карта не существует как статическая сущность: она представляет собой набор взаимосвязанных менеджеров, каждый из которых имеет собственный жизненный цикл.
DOM-контейнер карты становится центральной точкой привязки всех визуальных элементов:
При инициализации создаются внутренние слои-панели:
Каждая панель управляет своим типом DOM-узлов. Удаление карты требует очистки всех этих узлов, иначе остаются «осиротевшие» элементы.
Слой в Leaflet — это объект с собственным состоянием и методами
onAdd и onRemove.
При layer.addTo(map) происходит:
map._layersonAdd(map)Пример:
const marker = L.marker([51.5, -0.09]);
marker.addTo(map);
На этом этапе формируется полная связка: слой ↔︎ карта ↔︎ DOM ↔︎ события.
Удаление слоя через map.removeLayer(layer) или
layer.remove() инициирует обратный процесс:
onRemove(map)map._layersКритически важно, что Leaflet не всегда автоматически освобождает пользовательские ссылки внутри кастомных слоёв. Если слой содержит внешние объекты (например, WebSocket, таймеры), они должны быть очищены вручную.
Система событий Leaflet построена на подписках через on,
off, once.
map.on('moveend', handler);
map.off('moveend', handler);
Каждая подписка увеличивает внутренний список listeners. При
уничтожении карты отсутствие корректного off приводит к
сохранению замыканий и утечкам памяти.
Особое внимание требуется для:
movezoomresizeclicklayeradd, layerremove)TileLayer — один из самых ресурсоёмких компонентов.
При работе происходит:
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png')
.addTo(map);
Этапы жизненного цикла:
removeLayerБез явного удаления слоя сетевые запросы могут продолжаться даже после исчезновения карты из DOM.
Marker — объект с более сложной структурой:
При добавлении:
При удалении:
Особенно критично при использовании кастомных иконок, где создаются дополнительные DOM-узлы вне контроля Leaflet.
Popup и Tooltip имеют короткий жизненный цикл:
marker.bindPopup('Text');
marker.openPopup();
Жизненный цикл включает:
Утечки возникают при повторном открытии без корректного закрытия, если кастомные события продолжают ссылаться на popup.
GeoJSON layer агрегирует множество подслоёв:
L.geoJSON(data).addTo(map);
Внутренний процесс:
Жизненный цикл включает каскадное удаление:
При удалении GeoJSON слоя необходимо учитывать, что каждый дочерний слой живёт самостоятельной жизнью до момента очистки.
Обновления включают:
Ключевой метод:
map.invalidateSize();
Он перерасчитывает:
Отсутствие вызова после изменения DOM приводит к смещению тайлов и некорректной отрисовке.
При использовании React, Vue, Angular карта часто создаётся и уничтожается вместе с компонентом.
Основные этапы:
Критический этап:
map.remove()map.off();
map.remove();
map = null;
Без этого остаются:
Основные источники:
map.on('move', handler);
без off.
С внешними ресурсами:
С привязанными обработчиками.
Хранимые вручную элементы иконок.
Полное удаление включает:
map.eachLayer(layer => {
map.removeLayer(layer);
});
map.off();
map.remove();
После этого объект карты должен считаться невалидным для использования.
Каждая pane очищается отдельно:
Очистка происходит через DOM removal, но кастомные элементы, добавленные вручную, могут остаться.
Повторное создание карты на том же контейнере требует:
map.remove();
document.getElementById('map').innerHTML = '';
Иначе возникает конфликт внутренних состояний Leaflet.
Жизненный цикл можно представить как конечный автомат:
Переход в DESTROYED должен быть финальным: любые обращения к карте после него приводят к неопределённому поведению.
Tile loading использует:
При удалении карты: