Работа с картографическими сервисами в интерфейсах на Svelte требует
строгого разделения жизненного цикла карты и реактивной модели
фреймворка. API HERE Technologies построен вокруг императивного
управления объектами карты, тогда как Svelte ориентирован на
декларативное обновление состояния. Это различие определяет архитектуру
компонентов: карта существует как внешняя сущность, управляемая через
onMount, а Svelte отвечает за контейнер, конфигурацию и
реактивные параметры.
Карта HERE Maps создаётся после монтирования DOM-элемента, поскольку библиотека требует реального контейнера.
<script>
import { onMount } from 'svelte';
let mapContainer;
let map;
onMount(() => {
const platform = new H.service.Platform({
apikey: 'YOUR_API_KEY'
});
const defaultLayers = platform.createDefaultLayers();
map = new H.Map(
mapContainer,
defaultLayers.vector.normal.map,
{
center: { lat: 52.5200, lng: 13.4050 },
zoom: 10,
pixelRatio: window.devicePixelRatio || 1
}
);
const beh * avior = new H.mapevents.Behavior(
new H.mapevents.MapEvents(map)
);
H.ui.UI.createDefault(map, defaultLayers);
});
</script>
<div bind:this={mapContainer} class="map"></div>
<style>
.map {
width: 100%;
height: 500px;
}
</style>
Ключевой момент заключается в том, что объект карты создаётся строго
после появления DOM-узла. Любая попытка инициализации до
onMount приводит к отсутствию контейнера и некорректному
созданию canvas.
Svelte позволяет связать параметры карты с реактивными значениями, однако сама карта не является реактивной сущностью. Обновления выполняются вручную через API методов.
<script>
import { onMount } from 'svelte';
export let lat = 52.52;
export let lng = 13.405;
export let zoom = 10;
let mapContainer;
let map;
onMount(() => {
const platform = new H.service.Platform({ apikey: 'KEY' });
const layers = platform.createDefaultLayers();
map = new H.Map(mapContainer, layers.vector.normal.map, {
center: { lat, lng },
zoom
});
});
$: if (map) {
map.setCenter({ lat, lng });
}
$: if (map) {
map.setZoom(zoom);
}
</script>
<div bind:this={mapContainer} class="map"></div>
Реактивные блоки $: позволяют синхронизировать внешнее
состояние с картой, но важно учитывать, что каждое изменение вызывает
императивный вызов API, а не перерисовку компонента.
Для масштабируемых приложений карта оформляется как изолированный
компонент, принимающий параметры через props.
<script>
import { onMount, onDestroy } from 'svelte';
export let center = { lat: 0, lng: 0 };
export let zoom = 5;
let node;
let map;
onMount(() => {
const platform = new H.service.Platform({ apikey: 'KEY' });
const layers = platform.createDefaultLayers();
map = new H.Map(node, layers.vector.normal.map, {
center,
zoom
});
window.addEventListener('resize', handleResize);
});
function handleResize() {
if (map) map.getViewPort().resize();
}
onDestroy(() => {
if (map) {
map.dispose();
}
window.removeEventListener('resize', handleResize);
});
</script>
<div bind:this={node} class="map"></div>
Жизненный цикл Svelte используется для контроля ресурсов:
onDestroy обязателен для предотвращения утечек памяти, так
как HERE Maps создаёт WebGL и DOM-слои вне контроля фреймворка.
Маркер в HERE Maps представляет собой объект
H.map.Marker, который можно интегрировать в реактивную
структуру данных Svelte.
<script>
import { onMount } from 'svelte';
export let points = [];
let mapNode;
let map;
let markers = [];
onMount(() => {
const platform = new H.service.Platform({ apikey: 'KEY' });
const layers = platform.createDefaultLayers();
map = new H.Map(mapNode, layers.vector.normal.map, {
center: { lat: 0, lng: 0 },
zoom: 2
});
updateMarkers(points);
});
function updateMarkers(data) {
if (!map) return;
markers.forEach(m => map.removeObject(m));
markers = [];
data.forEach(p => {
const marker = new H.map.Marker({ lat: p.lat, lng: p.lng });
markers.push(marker);
map.addObject(marker);
});
}
$: updateMarkers(points);
</script>
<div bind:this={mapNode} class="map"></div>
Такая схема создаёт имитацию реактивности: изменение массива
points приводит к пересозданию слоя маркеров.
Частая ошибка заключается в полном пересоздании маркеров при каждом изменении данных. Это приводит к избыточной нагрузке на WebGL-слой.
Более эффективная модель использует дифференциальное обновление:
function syncMarkers(nextPoints) {
const key = p => `${p.lat}-${p.lng}`;
const nextMap = new Map(nextPoints.map(p => [key(p), p]));
markers.forEach(m => {
const id = key(m.getData?.() || {});
if (!nextMap.has(id)) {
map.removeObject(m);
}
});
nextPoints.forEach(p => {
const exists = markers.find(m => m.getData?.() === p);
if (!exists) {
const marker = new H.map.Marker(p);
marker.setData(p);
markers.push(marker);
map.addObject(marker);
}
});
}
HERE Maps поддерживает многослойную архитектуру: базовые тайлы, трафик, POI, кастомные слои. В Svelte это удобно организуется через реактивные переключатели.
<script>
export let trafficEnabled = false;
let map;
let layers;
$: if (map && layers) {
if (trafficEnabled) {
map.addLayer(layers.vector.traffic.map);
} else {
map.removeLayer(layers.vector.traffic.map);
}
}
</script>
Важно учитывать, что слои должны быть заранее инициализированы через
Platform.createDefaultLayers, иначе ссылки на объекты будут
отсутствовать.
События карты преобразуются в реактивные значения через обработчики.
<script>
import { createEventDispatcher, onMount } from 'svelte';
const dispatch = createEventDispatcher();
let mapNode;
let map;
onMount(() => {
const platform = new H.service.Platform({ apikey: 'KEY' });
const layers = platform.createDefaultLayers();
map = new H.Map(mapNode, layers.vector.normal.map, {
center: { lat: 0, lng: 0 },
zoom: 2
});
map.addEventListener('tap', (evt) => {
const coord = map.screenToGeo(
evt.currentPointer.viewportX,
evt.currentPointer.viewportY
);
dispatch('select', coord);
});
});
</script>
Таким образом, события HERE Maps преобразуются в события Svelte-компонента, что позволяет строить композицию интерфейса.
WebGL-контекст, создаваемый картой, требует явного освобождения ресурсов.
onDestroy(() => {
if (map) {
map.removeObjects(map.getObjects());
map.dispose();
}
});
Дополнительно требуется удаление всех внешних слушателей событий,
созданных через addEventListener, иначе они сохраняются вне
жизненного цикла компонента.
При усложнении интерфейса карта перестаёт быть единым компонентом и разделяется на логические части:
Каждый слой управляется отдельным Svelte-компонентом, но все они
используют общий экземпляр map, передаваемый через контекст
(setContext/getContext), что исключает дублирование
инициализации и обеспечивает централизованное управление состоянием
карты.