Svelte компоненты

Работа с картографическими сервисами в интерфейсах на Svelte требует строгого разделения жизненного цикла карты и реактивной модели фреймворка. API HERE Technologies построен вокруг императивного управления объектами карты, тогда как Svelte ориентирован на декларативное обновление состояния. Это различие определяет архитектуру компонентов: карта существует как внешняя сущность, управляемая через onMount, а Svelte отвечает за контейнер, конфигурацию и реактивные параметры.


Базовая инициализация карты в 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);
    }
  });
}

Слои и сервисы внутри Svelte-компонента

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, иначе ссылки на объекты будут отсутствовать.


Интеграция событий карты с Svelte-состоянием

События карты преобразуются в реактивные значения через обработчики.

<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, иначе они сохраняются вне жизненного цикла компонента.


Масштабирование архитектуры компонентов карты

При усложнении интерфейса карта перестаёт быть единым компонентом и разделяется на логические части:

  • контейнер карты
  • слой маркеров
  • слой маршрутов
  • UI-оверлеи
  • сервис синхронизации состояния

Каждый слой управляется отдельным Svelte-компонентом, но все они используют общий экземпляр map, передаваемый через контекст (setContext/getContext), что исключает дублирование инициализации и обеспечивает централизованное управление состоянием карты.