Mapbox GL JS реализует рендеринг карты через WebGL и работает с векторными и растровыми тайлами, которые загружаются динамически в зависимости от текущего состояния камеры. Подгрузка по требованию (lazy/on-demand loading) является базовым механизмом производительности, позволяющим минимизировать сетевой трафик, ускорять инициализацию карты и снижать нагрузку на браузер.
В основе работы лежит принцип: данные запрашиваются только тогда, когда они становятся потенциально видимыми или необходимыми для рендера.
Основные уровни подгрузки:
Ключевой механизм — параметры minzoom и
maxzoom:
map.addSource('cities', {
type: 'vector',
url: 'mapbox://examples.city-boundaries',
minzoom: 5,
maxzoom: 12
});
Поведение:
minzoom источник не запрашиваетсяmaxzoom используется интерполяция или прекращение
детализацииДля динамической загрузки данных применяется
GeoJSONSource:
map.addSource('dynamic-points', {
type: 'geojson',
data: {
type: 'FeatureCollection',
features: []
}
});
Обновление данных выполняется только при необходимости:
fetch('/api/points?bbox=' + map.getBounds().toArray().flat())
.then(res => res.json())
.then(data => {
map.getSource('dynamic-points').setData(data);
});
moveendОдин из базовых паттернов — загрузка данных после завершения перемещения камеры:
map.on('moveend', () => {
const bounds = map.getBounds();
fetch(`/api/features?bbox=${bounds.toArray().flat().join(',')}`)
.then(res => res.json())
.then(data => {
map.getSource('dynamic-points').setData(data);
});
});
Особенности:
Для более агрессивной оптимизации используется задержка:
let timeout;
map.on('move', () => {
clearTimeout(timeout);
timeout = setTimeout(() => {
const bounds = map.getBounds();
fetch(`/api/features?bbox=${bounds.toArray().flat()}`)
.then(res => res.json())
.then(data => {
map.getSource('dynamic-points').setData(data);
});
}, 300);
});
Mapbox GL JS предоставляет механизмы контроля состояния источников.
isSourceLoadedif (map.isSourceLoaded('cities')) {
console.log('Источник уже загружен');
}
sourceData событиеmap.on('sourcedata', (e) => {
if (e.isSourceLoaded) {
console.log('Источник полностью загружен');
}
});
Слои можно включать и отключать в зависимости от контекста.
map.addLayer({
id: 'city-layer',
type: 'circle',
source: 'dynamic-points',
paint: {
'circle-radius': 6,
'circle-color': '#ff5500'
}
});
map.setLayoutProperty('city-layer', 'visibility', 'none');
Подход предпочтительнее удаления:
Одним из наиболее эффективных методов является загрузка только видимой области.
function loadVisibleFeatures() {
const bbox = map.getBounds();
fetch('/api/features', {
method: 'POST',
body: JSON.stringify({ bbox })
})
.then(res => res.json())
.then(data => {
map.getSource('dynamic-points').setData(data);
});
}
Использование:
Mapbox GL JS поддерживает асинхронное добавление ресурсов в sprite-подобную систему.
loadImagemap.loadImage('/icons/marker.png', (error, image) => {
if (!error) {
map.addImage('custom-marker', image);
}
});
Поведение:
Шрифты загружаются автоматически при появлении текстовых слоев:
map.addLayer({
id: 'labels',
type: 'symbol',
source: 'cities',
layout: {
'text-field': ['get', 'name']
}
});
Механизм:
renderWorldCopiesmap.setRenderWorldCopies(false);
Влияние:
setStyle
как механизм ленивой смены данныхmap.setStyle('mapbox://styles/mapbox/dark-v11');
Особенности:
idle как сигнала завершения загрузкиmap.on('idle', () => {
console.log('Все видимые ресурсы загружены');
});
Смысл:
Вместо загрузки новых данных можно изменять фильтры слоя:
map.setFilter('city-layer', ['>', ['get', 'population'], 100000]);
Преимущества:
map.setFeatureState(
{ source: 'cities', id: 123 },
{ selected: true }
);
Использование:
map.on('click', (e) => {
const features = map.queryRenderedFeatures(e.point, {
layers: ['city-layer']
});
if (features.length) {
loadDetails(features[0].properties.id);
}
});
Типичная проблема — дублирование загрузок при частых событиях камеры.
Решение через кеширование:
const cache = new Set();
function loadTile(id) {
if (cache.has(id)) return;
cache.add(id);
fetch(`/api/tile/${id}`)
.then(res => res.json())
.then(data => {
// обработка
});
}
moveend, idle,
sourcedata используются как триггеры