Tree shaking в экосистеме фронтенд-сборки означает автоматическое удаление неиспользуемого кода из итогового бандла. В случае с Leaflet этот механизм имеет специфические ограничения, связанные с историей библиотеки, её модульной структурой и характером побочных эффектов в геоинтерактивных API.
Leaflet изначально проектировался как компактная UMD-библиотека,
ориентированная на глобальный объект L. Позднее появилась
поддержка ES Modules, однако не все части API одинаково хорошо поддаются
статическому анализу сборщиками.
Классическая сборка Leaflet опирается на единый namespace:
import L from 'leaflet';
const map = L.map('map');
В этом случае сборщик воспринимает L как единый объект
без возможности безопасно удалить части его содержимого. Причина —
динамическая структура API и наличие побочных эффектов при инициализации
модулей.
Современные версии предоставляют частичную ESM-структуру:
import { Map, TileLayer } from 'leaflet';
Однако не все внутренние классы и утилиты экспортируются как чистые функции без side effects, что ограничивает эффективность tree shaking.
Tree shaking в Leaflet становится предсказуемым только при выполнении ряда условий:
sideEffects: false в конфигурации пакета
(частично применимо)Сборщики типа Vite, Rollup и Webpack (в production mode) используют статический анализ импортов:
import { Map, TileLayer, Marker } from 'leaflet';
Если код не использует Marker, он потенциально может
быть исключён, но только при условии, что модуль Leaflet корректно
размечен как tree-shakable.
ESM-версия Leaflet предоставляет более гранулярные точки входа:
import { Map } from 'leaflet';
import { TileLayer } from 'leaflet';
Такой стиль импорта улучшает анализ зависимостей, однако не гарантирует минимальный бандл. Причина заключается в том, что многие компоненты Leaflet имеют косвенные зависимости от core-инициализации.
Например, регистрация событий, классов и прототипных расширений выполняется при загрузке модуля, что делает часть кода «неудаляемой» с точки зрения tree shaking.
В современных сборках Leaflet чаще всего используется через ESM:
import { Map, TileLayer, Marker } from 'leaflet';
const map = new Map('map');
map.setView([51.505, -0.09], 13);
new TileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png')
.addTo(map);
Vite использует Rollup под капотом, что обеспечивает более агрессивное удаление неиспользуемых экспортов. Webpack требует корректной настройки production mode:
module.exports = {
mode: 'production',
optimization: {
usedExports: true,
sideEffects: true
}
};
Однако даже при включённом usedExports Leaflet может
частично сохранять неиспользуемые части API.
Leaflet содержит обязательные стилевые зависимости:
import 'leaflet/dist/leaflet.css';
CSS импорт не участвует в tree shaking, но влияет на финальный размер сборки через style injection или отдельный CSS chunk.
Кроме того, инициализация иконок:
import iconUrl from 'leaflet/dist/images/marker-icon.png';
создаёт дополнительные ассеты, которые не могут быть удалены tree shaking-механизмом, поскольку они не являются частью JS-графа зависимостей.
Некоторые конструкции Leaflet принципиально плохо поддаются статической оптимизации:
L.map,
L.tileLayer)Пример:
import 'leaflet.markercluster';
Такой импорт всегда считается побочным эффектом и не может быть удалён даже при отсутствии явного использования.
Экосистема Leaflet активно использует плагины, которые почти всегда нарушают tree shaking-модель:
import 'leaflet-draw';
import 'leaflet-routing-machine';
Каждый плагин выполняет регистрацию глобальных расширений в runtime, что делает невозможным частичное удаление кода.
В результате итоговый бандл определяется не только импортами основного пакета, но и транзитивными зависимостями плагинов.
Наиболее устойчивые подходы к снижению размера сборки:
import { Map, TileLayer } from 'leaflet';
Минимизация использования глобального L снижает
вероятность подтягивания всего namespace.
import { Map } from 'leaflet/src/map';
import { TileLayer } from 'leaflet/src/layer/tile';
Такой подход зависит от структуры исходного кода пакета и может быть нестабильным между версиями.
let map;
function initMap() {
const { Map } = require('leaflet');
map = new Map('map');
}
Динамический require исключает tree shaking, но
позволяет сегментировать загрузку.
Tree shaking Leaflet напрямую зависит от стратегии сборки:
Ключевым фактором остаётся корректная маркировка пакета как ESM-only или hybrid.
Распространённые проблемы:
L.*Leaflet как геобиблиотека содержит базовый runtime, который практически всегда остаётся в финальном бандле, поскольку он необходим для работы карты и DOM-интеграции.