Установка Leaflet в современном фронтенд-проекте на Vite начинается с подключения зависимостей и подготовки структуры приложения. Vite использует ES-модули и мгновенную сборку через dev-сервер, что делает работу с картографическими библиотеками более предсказуемой и быстрой по сравнению с классическими бандлерами.
Установка:
npm create vite@latest leaflet-vite-app
cd leaflet-vite-app
npm install
npm install leaflet
Дополнительно часто требуется установка типов (если используется TypeScript):
npm install -D @types/leaflet
Библиотека Leaflet не требует дополнительных плагинов для базовой работы, однако в связке с Vite важно учитывать особенности сборки статических ресурсов, особенно иконок маркеров и CSS.
Leaflet не работает корректно без подключения CSS. В Vite стили импортируются напрямую из node_modules.
import L from 'leaflet';
import 'leaflet/dist/leaflet.css';
Инициализация карты выполняется после того, как DOM-элемент уже существует:
const map = L.map('map').setView([51.505, -0.09], 13);
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 19,
attribution: '© OpenStreetMap contributors'
}).addTo(map);
HTML-структура:
<div id="map"></div>
CSS для контейнера карты критически важен:
#map {
height: 100vh;
width: 100%;
}
Без явной высоты контейнер будет иметь нулевую высоту, и карта не отобразится.
Vite использует ESM-модули в режиме разработки и Rollup для production-сборки. Это влияет на работу Leaflet в нескольких аспектах:
Leaflet изначально проектировался для классических бандлеров и CDN, поэтому некоторые внутренние пути (особенно иконки) требуют корректировки.
Одна из типичных проблем — отсутствие стандартных иконок маркеров. В production-сборке Vite не сохраняет пути Leaflet к изображениям автоматически.
Решение заключается в ручной переопределении путей:
import L from 'leaflet';
import iconUrl from 'leaflet/dist/images/marker-icon.png';
import iconRetinaUrl from 'leaflet/dist/images/marker-icon-2x.png';
import shadowUrl from 'leaflet/dist/images/marker-shadow.png';
delete L.Icon.Default.prototype._getIconUrl;
L.Icon.Default.mergeOptions({
iconRetinaUrl,
iconUrl,
shadowUrl
});
Это обеспечивает корректную работу иконок как в dev, так и в production-сборке.
Vite использует папку public/ для статических файлов,
которые должны быть доступны без обработки сборщиком.
Структура:
public/
images/
data/
Пример использования кастомной иконки:
const customIcon = L.icon({
iconUrl: '/images/custom-marker.png',
iconSize: [32, 32],
iconAnchor: [16, 32]
});
L.marker([51.5, -0.09], { icon: customIcon }).addTo(map);
Важно: путь начинается с /, так как Vite копирует
содержимое public в корень сборки.
В приложениях с компонентной архитектурой (React, Vue, Svelte) карта должна инициализироваться после монтирования DOM.
Пример логики:
import { useEffect } from 'react';
import L from 'leaflet';
import 'leaflet/dist/leaflet.css';
export default function MapComponent() {
useEffect(() => {
const map = L.map('map').setView([51.505, -0.09], 13);
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 19
}).addTo(map);
return () => {
map.remove();
};
}, []);
return <div id="map" style={{ height: '100vh' }} />;
}
Удаление карты при размонтировании важно для предотвращения утечек памяти.
Vite обеспечивает мгновенное обновление модулей без полной перезагрузки страницы. В контексте Leaflet это влияет на:
Если карта пересоздаётся при HMR, требуется явное удаление предыдущего экземпляра:
if (import.meta.hot) {
import.meta.hot.dispose(() => {
map.remove();
});
}
Без этого возможно накопление слоёв и некорректное отображение тайлов.
Leaflet в Vite не ограничивает выбор источников тайлов. Наиболее распространённый вариант — OpenStreetMap.
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 19,
updateWhenIdle: true,
keepBuffer: 2
}).addTo(map);
Оптимизационные параметры:
keepBuffer — снижает количество перерисовокupdateWhenIdle — обновляет тайлы только при остановке
перемещенияmaxZoom — ограничивает детализациюVite способствует разделению логики на модули, что удобно для работы с слоями карты.
Пример организации:
src/
map/
createMap.js
layers.js
markers.js
createMap.js:
import L from 'leaflet';
export function createMap(containerId) {
return L.map(containerId).setView([51.505, -0.09], 13);
}
layers.js:
import L from 'leaflet';
export function createBaseLayer() {
return L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 19
});
}
Такой подход упрощает масштабирование приложения.
Vite при сборке может размещать приложение не в корне домена. Leaflet требует корректной настройки base path для ассетов.
В vite.config.js:
import { defineConfig } from 'vite';
export default defineConfig({
base: '/app/'
});
Если используется кастомный base, важно учитывать это при загрузке
ресурсов из public.
Leaflet может быть вынесен в динамический импорт для уменьшения начального бандла.
async function initMap() {
const L = await import('leaflet');
await import('leaflet/dist/leaflet.css');
const map = L.map('map').setView([51.505, -0.09], 13);
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(map);
}
Vite автоматически создаёт отдельный chunk для Leaflet, уменьшая размер initial bundle.
Vite использует .env файлы с префиксом
VITE_.
Пример:
VITE_TILE_URL=https://tile.openstreetmap.org/{z}/{x}/{y}.png
Использование:
const tileUrl = import.meta.env.VITE_TILE_URL;
L.tileLayer(tileUrl).addTo(map);
Это позволяет переключать источники тайлов между окружениями без изменения кода.
Leaflet зависит от браузерного DOM и не может выполняться на сервере. При использовании SSR-фреймворков с Vite (например, Nuxt или custom SSR) требуется изоляция:
if (typeof window !== 'undefined') {
const L = await import('leaflet');
}
Или полное отключение выполнения на сервере.
При увеличении количества маркеров и слоёв важны следующие аспекты:
leaflet.markercluster)Пример кластеризации:
import 'leaflet.markercluster';
const markers = L.markerClusterGroup();
markers.addLayer(L.marker([51.5, -0.09]));
markers.addTo(map);
Leaflet в TypeScript требует явного указания типов для карты и слоёв.
import L, { Map } from 'leaflet';
let map: Map;
map = L.map('map').setView([51.505, -0.09], 13);
Типы помогают избежать ошибок при работе с событиями и кастомными слоями:
map.on('click', (e: L.LeafletMouseEvent) => {
console.log(e.latlng);
});
Экосистема Leaflet включает множество плагинов, совместимых с Vite без дополнительной конфигурации:
Пример подключения heatmap:
import 'leaflet.heat';
L.heatLayer([[51.5, -0.09, 0.5]]).addTo(map);
При построении сложных приложений карта рассматривается как независимый сервис:
Такой подход снижает связанность кода и упрощает интеграцию с Vite-плагинами и HMR-циклом.