Оптимизация производительности

Mapbox GL JS построен поверх Mapbox GL JS и использует WebGL для отрисовки векторных данных. Основной цикл производительности определяется тремя компонентами: загрузка данных, подготовка стиля и GPU-рендеринг.

Ключевая особенность архитектуры заключается в разделении на worker-потоки и главный поток. Данные (GeoJSON, vector tiles) обрабатываются в Web Worker, однако любые тяжёлые операции на стороне JavaScript (пересборка стилей, частые вызовы API, массовые обновления источников) способны блокировать главный поток и снижать FPS.


Оптимизация источников данных

Сокращение объёма GeoJSON

Наиболее частая причина деградации производительности — перегруженные GeoJSON-источники. Даже при использовании WebGL избыточная геометрия приводит к росту времени:

  • парсинга JSON
  • триангуляции полигонов
  • загрузки в GPU buffers

Практика оптимизации:

  • удаление лишних атрибутов properties
  • упрощение геометрии (Douglas–Peucker)
  • предварительная агрегация данных на сервере
  • замена GeoJSON на vector tiles при больших объёмах

Особенно критично избегать передачи тысяч точек в одном объекте LineString — такой сценарий вызывает деградацию при каждом обновлении источника.


Использование vector tiles вместо GeoJSON

Vector tiles позволяют делить данные на тайлы и загружать только видимую область. Это снижает:

  • сетевой трафик
  • нагрузку на парсер
  • GPU memory footprint

При переходе на тайловую модель уменьшается необходимость в повторных перерисовках при панорамировании карты.


Оптимизация слоёв (layers)

Минимизация количества слоёв

Каждый слой в стиле увеличивает стоимость:

  • оценки выражений filter
  • вычисления layout
  • рендера в отдельном draw call

Рекомендуется:

  • объединять слои с одинаковым стилем
  • использовать match и case вместо множества слоёв
  • избегать дублирующих слоёв для разных zoom-уровней

Ограничение диапазона zoom

Использование minzoom и maxzoom резко снижает количество отрисовываемых объектов:

  • исключаются слои вне диапазона
  • уменьшается количество tile requests
  • снижается нагрузка на GPU

Упрощение выражений стиля

Сложные expressions (особенно nested case, interpolate, coalesce) увеличивают стоимость вычисления кадра.

Оптимизация:

  • перенос логики в preprocessing данных
  • сокращение вложенности условий
  • замена вычислений на атрибуты properties

Управление рендерингом и FPS

Использование событий moveend вместо move

Событие move вызывается на каждый кадр, что может приводить к перегрузке логики:

  • фильтрация данных
  • обновление источников
  • запросы к API

Более эффективный подход:

  • moveend для финальных обновлений
  • idle для тяжёлых операций

Throttling и debouncing API-вызовов

Любые изменения через:

  • setFilter
  • setData
  • setPaintProperty

должны быть ограничены по частоте. Без контроля вызовы приводят к постоянному пересбору слоя.


Минимизация setStyle

Полная пересборка стиля — одна из самых дорогих операций. Она приводит к:

  • уничтожению всех WebGL буферов
  • повторной загрузке источников
  • пересозданию слоёв

Вместо этого предпочтительно:

  • setPaintProperty
  • setLayoutProperty
  • частичное обновление источников

Оптимизация символов и текстов

Управление столкновениями (collision detection)

Символы (text, icons) являются одними из самых дорогих элементов рендера.

Настройки:

  • text-allow-overlap
  • icon-allow-overlap

уменьшают вычисления коллизий, но увеличивают визуальный шум. Баланс зависит от сценария.


Использование zoom-dependent styles

Text-size и icon-size можно задавать через zoom stops:

  • уменьшается количество пересчётов layout
  • GPU работает с предсказуемыми значениями

Пример логики:

  • на низком zoom — только ключевые подписи
  • на высоком zoom — детализация

Ограничение количества символов

Чрезмерное количество label-слоёв резко снижает FPS. Оптимальные практики:

  • агрегация названий объектов
  • генерация кластерных подписей
  • фильтрация по важности (rank property)

Кластеризация данных

Для точечных данных кластеризация является одним из наиболее эффективных методов оптимизации.

Преимущества:

  • уменьшение числа объектов на карте
  • ускорение collision detection
  • снижение GPU нагрузки

Ключевые параметры:

  • cluster: true
  • clusterRadius
  • clusterMaxZoom

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


Оптимизация изображений и иконок

Sprite sheet вместо отдельных изображений

Использование sprite atlas снижает:

  • количество HTTP-запросов
  • задержки загрузки
  • переключение текстур на GPU

Сжатие изображений

Использование форматов:

  • WebP
  • PNG с оптимизацией палитры

уменьшает VRAM потребление и ускоряет загрузку стилей.


Работа с тайлами

Кэширование

Mapbox GL JS имеет встроенный кэш тайлов, однако его эффективность зависит от:

  • стабильности URL источников
  • отсутствия случайных query параметров
  • правильной настройки transformRequest

Ограничение overscaling

Избыточное масштабирование тайлов увеличивает нагрузку GPU:

  • каждый тайл рендерится несколько раз
  • растёт количество draw calls

Оптимально ограничивать zoom диапазоны данных.


Управление источниками данных

Переиспользование источников

Создание и удаление источников — дорогая операция. Более эффективный подход:

  • один source → несколько layers
  • обновление через setData, а не пересоздание

Асинхронная загрузка данных

Загрузка данных должна быть:

  • ленивой (lazy loading)
  • привязанной к viewport

Использование bounds-based запросов уменьшает сетевую нагрузку.


GPU и память

Контроль WebGL контекста

Проблемы производительности часто связаны с утечками GPU памяти:

  • создание множества карт без remove
  • пересоздание текстур
  • отсутствие cleanup при смене страниц

Ограничение количества одновременно отображаемых карт

Каждый экземпляр карты:

  • создаёт WebGL context
  • резервирует GPU память
  • увеличивает нагрузку на compositor

Рекомендуется поддерживать минимально возможное количество активных экземпляров.


Геометрическая оптимизация

Упрощение линий и полигонов

Сложные геометрии:

  • увеличивают vertex count
  • замедляют триангуляцию
  • ухудшают panning performance

Используемые методы:

  • Douglas–Peucker simplification
  • pre-simplified LOD версии данных

LOD (Level of Detail)

Разные уровни детализации данных:

  • низкий zoom → упрощённые формы
  • высокий zoom → детальные геометрии

Позволяет значительно снизить нагрузку на GPU без потери качества.


Сетевые оптимизации

Снижение количества запросов

Причины избыточных запросов:

  • динамическая генерация URL
  • частые setData
  • отсутствие tile caching

Решения:

  • стабильные endpoint URL
  • HTTP caching headers
  • batching запросов

Prefetching видимых областей

Заранее загружаются тайлы вокруг viewport:

  • снижает задержки при pan
  • уменьшает “white tiles”

Поведенческая оптимизация API

Ограничение частоты обновлений состояния

Любые интерактивные изменения (hover, drag, select):

  • должны быть debounced
  • должны использовать requestAnimationFrame

Иначе главный поток перегружается обработчиками событий.


Разделение логики UI и карты

Смешивание UI-логики и map logic приводит к:

  • избыточным rerender
  • конфликтам состояния
  • росту CPU usage

Оптимально держать карту как изолированный рендеринг-слой.


Профилирование производительности

Ключевые метрики:

  • FPS (Frames Per Second)
  • tile load time
  • style evaluation time
  • GPU memory usage

Инструменты:

  • Chrome Performance panel
  • WebGL inspector
  • Mapbox telemetry hooks

Регулярный анализ помогает выявлять:

  • дорогие слои
  • перегруженные источники
  • избыточные события render loop