Leaflet строится вокруг асинхронной загрузки ресурсов: тайлов, GeoJSON-данных, векторных слоёв и внешних API. Большая часть проблем в реальных приложениях связана не с логикой карты, а с сетевыми сбоями, недоступностью тайловых серверов, ошибками CORS и некорректными ответами от источников данных.
Механика обработки ошибок в Leaflet распределена по нескольким уровням:
Эти уровни не конкурируют между собой, а дополняют друг друга.
tileerrorНаиболее частая проблема — недоступные тайлы в
TileLayer. В Leaflet для этого используется событие
tileerror, которое срабатывает при невозможности загрузки
изображения тайла.
const map = L.map('map').setView([51.505, -0.09], 13);
const tiles = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 19
});
tiles.on('tileerror', function (error, tile) {
console.log('Ошибка загрузки тайла:', error, tile);
});
Событие предоставляет контекст:
Распространённая стратегия — подмена изображения при ошибке:
tiles.on('tileerror', function (error, tile) {
tile.tile.src = '/images/fallback-tile.png';
});
Подход позволяет избежать «дыр» на карте, особенно при нестабильных серверах.
errorTileUrlLeaflet поддерживает встроенный механизм fallback-тайла:
const tiles = L.tileLayer('https://example.com/{z}/{x}/{y}.png', {
errorTileUrl: '/images/error-tile.png'
});
Особенность:
Нативного retry-механизма нет, но он легко реализуется вручную:
tiles.on('tileerror', function (error, tile) {
let retries = tile._retryCount || 0;
if (retries < 3) {
tile._retryCount = retries + 1;
setTimeout(() => {
tile.src = tile.src;
}, 500);
} else {
tile.src = '/images/fallback.png';
}
});
Такая логика полезна при нестабильных CDN.
GeoJSON часто подгружается отдельно от карты:
fetch('/data/regions.geojson')
.then(res => res.json())
.then(data => {
L.geoJSON(data).addTo(map);
})
.catch(err => {
console.error('Ошибка загрузки GeoJSON:', err);
});
Здесь ошибка возникает до передачи данных в Leaflet, поэтому
обработка полностью лежит на fetch.
Даже успешный HTTP-запрос не гарантирует корректность GeoJSON:
function isValidGeoJSON(data) {
return data &&
data.type === 'FeatureCollection' &&
Array.isArray(data.features);
}
Использование:
fetch('/data.geojson')
.then(r => r.json())
.then(data => {
if (!isValidGeoJSON(data)) {
throw new Error('Некорректный GeoJSON');
}
L.geoJSON(data).addTo(map);
})
.catch(console.error);
L.geoJSONLeaflet позволяет обрабатывать ошибки на уровне каждого feature через
onEachFeature, но прямого error callback нет. Поэтому
применяется защитное программирование:
L.geoJSON(data, {
onEachFeature: function (feature, layer) {
try {
if (feature.properties) {
layer.bindPopup(feature.properties.name);
}
} catch (e) {
console.error('Ошибка обработки feature:', e, feature);
}
}
}).addTo(map);
Одна из частых причин сбоев — отсутствие DOM-элемента:
const container = document.getElementById('map');
if (!container) {
throw new Error('Контейнер карты не найден');
}
const map = L.map(container);
Leaflet не создаёт карту без валидного контейнера, но выбрасываемая ошибка не всегда очевидна в сложных приложениях.
Повторный вызов L.map() на одном элементе приводит к
конфликтам:
if (map) {
map.remove();
}
const map = L.map('map');
Удаление предыдущего экземпляра критично при SPA-навигации.
При загрузке тайлов и внешних данных возникают:
Leaflet не обрабатывает их автоматически, так как работает поверх браузерных API.
tiles.on('loading', () => console.log('Начало загрузки тайлов'));
tiles.on('load', () => console.log('Тайлы загружены'));
tiles.on('tileerror', (err) => console.log('Ошибка тайла', err));
Комбинация событий позволяет локализовать проблему:
loading без load → зависание сетиtileerror → недоступный серверОшибки часто возникают в style функциях:
L.geoJSON(data, {
style: function (feature) {
try {
return {
color: feature.properties.color || 'blue'
};
} catch (e) {
return { color: 'gray' };
}
}
}).addTo(map);
Leaflet вызывает style для каждого feature, поэтому даже
одна ошибка может прервать отрисовку части слоя.
const layer = L.geoJSON(data);
layer.on('add', () => console.log('Слой добавлен'));
layer.on('remove', () => console.log('Слой удален'));
Хотя эти события не являются ошибками, они часто используются для контроля состояния после сбоя загрузки.
try {
const map = L.map('map');
L.tileLayer(url).addTo(map);
} catch (e) {
document.getElementById('map').innerHTML =
'Ошибка инициализации карты';
}
Leaflet редко бросает синхронные ошибки, но защита полезна при динамической конфигурации.
function createOfflineMap() {
const map = L.map('map');
L.rectangle([[0,0],[0,0]], {
color: '#ccc'
}).addTo(map);
return map;
}
Такой режим применяется, когда тайловые сервисы недоступны полностью.
function attachDebug(layer) {
layer.on('tileerror', e => console.warn('tileerror', e));
layer.on('loading', () => console.info('loading'));
layer.on('load', () => console.info('load'));
}
Leaflet позволяет навешивать обработчики на любые слои, что делает возможным централизованную диагностику.
function validateTileUrl(url) {
try {
new URL(url);
return true;
} catch {
return false;
}
}
Перед созданием TileLayer такая проверка предотвращает
множество скрытых ошибок.
map.whenReady(() => {
console.log('Карта готова');
});
Этот метод особенно важен при асинхронной загрузке конфигурации.
{z}/{x}/{y}Leaflet в этих случаях не скрывает проблему, а передаёт её в браузерный слой, поэтому диагностика всегда требует работы с событиями и сетевыми инструментами.