При увеличении числа объектов на карте Leaflet основное узкое место
возникает не в загрузке данных, а в их отрисовке и управлении
DOM-элементами. Каждый маркер (L.marker) в стандартной
реализации представляет собой отдельный HTML-элемент, привязанный к слою
карты. Это означает, что при добавлении тысяч точек браузер вынужден
поддерживать тысячи DOM-узлов, обрабатывать их позиционирование и
события.
Ключевые ограничения стандартного подхода:
Даже при сравнительно мощном устройстве порог в 3–5 тысяч маркеров может приводить к заметным лагам без оптимизации.
Каждый маркер в Leaflet проходит несколько этапов:
L.marker(latlng).addTo(map)const marker = L.marker([51.505, -0.09]).addTo(map);
При масштабировании системы до тысяч объектов основной расход ресурсов приходится на этапы 3–5.
Leaflet по умолчанию использует HTML-рендеринг маркеров. Это удобно, но плохо масштабируется.
Проблема заключается в том, что:
В результате возникает квадратичное ухудшение производительности при увеличении плотности объектов.
Первый уровень оптимизации — отказ от ненужных интерактивных возможностей.
L.marker([51.5, -0.09], {
interactive: false
}).addTo(map);
Отключение интерактивности снижает количество event listener-ов и уменьшает нагрузку на систему событий Leaflet.
Также полезно отключать:
L.marker([51.5, -0.09], {
keyboard: false,
title: ''
});
pane для оптимизации слоёвLeaflet позволяет управлять слоями через panes, что влияет на порядок отрисовки и частично на производительность.
map.createPane('markersPane');
map.getPane('markersPane').style.zIndex = 650;
const marker = L.marker([51.5, -0.09], {
pane: 'markersPane'
}).addTo(map);
Разделение маркеров по слоям позволяет:
Основной метод масштабирования — объединение маркеров в кластеры.
Используется библиотека:
leaflet.markerclusterПринцип работы:
const markers = L.markerClusterGroup();
markers.addLayer(L.marker([51.5, -0.09]));
markers.addLayer(L.marker([51.51, -0.1]));
map.addLayer(markers);
Преимущества:
Альтернативный подход — рендеринг только видимой области.
Принцип:
function renderVisibleMarkers() {
const bounds = map.getBounds();
markersData.forEach(point => {
if (bounds.contains(point.latlng)) {
L.marker(point.latlng).addTo(map);
}
});
}
Обновление при перемещении карты:
map.on('moveend', renderVisibleMarkers);
Преимущество:
Недостаток:
Leaflet поддерживает Canvas как альтернативу HTML-слою через
L.canvas().
const canvasRenderer = L.canvas();
L.circleMarker([51.5, -0.09], {
renderer: canvasRenderer
}).addTo(map);
Особенности:
Однако:
circleMarker вместо markerL.circleMarker менее затратен, чем
L.marker, поскольку не создаёт HTML-иконку.
L.circleMarker([51.5, -0.09], {
radius: 5
}).addTo(map);
Причины высокой производительности:
Добавление тысяч маркеров за один цикл блокирует основной поток.
Проблема:
for (let i = 0; i < 10000; i++) {
L.marker(getRandomLatLng()).addTo(map);
}
Оптимизация через батчинг:
let i = 0;
function addBatch() {
const batchSize = 200;
for (let j = 0; j < batchSize && i < data.length; j++, i++) {
L.marker(data[i].latlng).addTo(map);
}
if (i < data.length) {
requestAnimationFrame(addBatch);
}
}
addBatch();
Преимущество:
LayerGroup для управления множеством объектовLayerGroup позволяет группировать маркеры и управлять
ими как единым объектом.
const group = L.layerGroup();
data.forEach(item => {
group.addLayer(L.marker(item.latlng));
});
map.addLayer(group);
Преимущества:
Кастомные иконки часто становятся причиной падения производительности.
Проблемные случаи:
Оптимизированный вариант:
const icon = L.icon({
iconUrl: 'marker.png',
iconSize: [16, 16],
iconAnchor: [8, 8]
});
Рекомендации по снижению нагрузки:
При интенсивных обновлениях (например, поток GPS-данных) необходимо ограничивать частоту перерисовки.
let lastUpdate = 0;
function updateMarkers() {
const now = Date.now();
if (now - lastUpdate < 100) return;
lastUpdate = now;
// обновление маркеров
}
Это предотвращает перегрузку render loop.
DOM-маркеры
ClusterGroup
Canvas renderer
CircleMarker
Виртуализация
На практике используется сочетание нескольких методов:
Такой подход позволяет поддерживать стабильную работу карты даже при десятках и сотнях тысяч точек без существенной деградации производительности.