Vitepress e leaflet.markercluster
Tags: javascript, typescript, vue, maps
Published: 2024-05-21 18:22:46

## Vitepress e leaflet.markercluster

[VitePress esegue il pre-rendering dell'app in Node.js durante la build di produzione, utilizzando le funzionalità di rendering lato server (SSR) di Vue](https://vitepress.dev/guide/ssr-compat): questo fatto impedisce di usare `leaflet.markercluster` all'interno di un progetto vitepress. Supponiamo di creare questa semplice mappa leaflet all'interno di un componente vue a sua volta utilizzato in un progetto vitepress:

```vue
<template>
  <div id="map">
    <l-map
      :zoom="9"
      :center="[45.1, 9.1]"
      :use-global-leaflet="true"
    >
      <l-tile-layer url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" :attribution="attribution"/>
      <l-marker-cluster-group>
        <l-marker :lat-lng="[45.1, 9.1]"/>
        <l-marker :lat-lng="[45.2, 9.2]"/>
        <l-marker :lat-lng="[45.3, 9.3]"/>
      </l-marker-cluster-group>
    </l-map>
  </div>
</template>

<script setup lang="ts">
import {LMarker, LMap, LTileLayer} from "@vue-leaflet/vue-leaflet";
import L, {marker} from 'leaflet'

globalThis.L = L
import {LMarkerClusterGroup} from 'vue-leaflet-markercluster'
import {ref} from 'vue'

import 'leaflet/dist/leaflet.css'
import 'vue-leaflet-markercluster/dist/style.css'

const attribution = ref("openstreetmap contributors")
const prefix = ref("leaflet")
</script>

<style scoped>
#map {
  height: 50vh;
  width: 100%;
}
</style>
```

Mentre `yarn docs:dev` o `pnpm docs:dev` (alias di `vitepress dev docs`, si veda la [documentazione vitepress](https://vitepress.dev/guide/getting-started#up-and-running)) eseguono con successo il codice qui sopra, la fase di build fallisce a causa appunto dell'utilizzo di SSR da parte di `leaflet.markercluster`.
Fortunatamente è possibile evitare il problema utilizzando la sintassi [`import()`, comunemente definitita "import dinamico"](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import). Esistono diverse implementazioni possibili (ad es. [async components](https://vuejs.org/guide/components/async.html)) ma in questo caso preferisco utilizzare la classica sintassi javascript all'interno dell'hook `onBeforeMount()`. Si può quindi eliminare il malfunzionamento in questo modo:

```vue
<template>
  <div id="map-photos"/>
</template>

<script setup lang="ts">
import "leaflet/dist/leaflet.css";
import "leaflet.markercluster/dist/MarkerCluster.css"
import "leaflet.markercluster/dist/MarkerCluster.Default.css"
import {onBeforeMount} from "vue";

onBeforeMount(async () => {
  const L = await import("leaflet")
  const {MarkerClusterGroup} = await import('leaflet.markercluster/dist/leaflet.markercluster')

  const map = L.map("map-photos") // div id
  map.setView([45.1, 9.1], 8)
  L.tileLayer("http://{s}.tile.osm.org/{z}/{x}/{y}.png").addTo(map);
  let mcluster = new MarkerClusterGroup()
  let marker1 = new L.Marker([45.1, 9.1])
  let marker2 = new L.Marker([45.2, 9.2])
  let marker3 = new L.Marker([45.3, 9.3])
  marker1.addTo(mcluster)
  marker2.addTo(mcluster)
  marker3.addTo(mcluster)
  mcluster.addTo(map)
})
</script>

<style scoped>
#map-photos {
  width: 100%;
  height: 60vh;
}
</style>
```

Si noti che `onBeforeMount()`  deve esser asincrono per funzionare.
Ultimo ma non per importanza il package.json di questo progetto vitepress minimale deve contenere queste dipendenze:

```json
{
  "name": "vitepress-ssr-build",
  "version": "1.0.0",
  "scripts": {
    "docs:dev": "vitepress dev docs",
    "docs:build": "pnpm i --frozen-lockfile && vitepress build docs",
    "docs:serve": "vitepress serve docs",
    "docs:preview": "vitepress preview docs --port 3000"
  },
  "license": "MIT",
  "type": "module",
  "dependencies": {
    "@vue-leaflet/vue-leaflet": "^0.10.1",
    "leaflet": "^1.9.4",
    "leaflet.markercluster": "^1.5.3",
    "vitepress": "^1.0.0-rc.10",
    "vue": "^3.3.4"
  },
  "devDependencies": {
    "@tsconfig/recommended": "^1.0.2",
    "@types/geojson": "^7946.0.10",
    "@types/leaflet": "^1.9.4",
    "@types/leaflet.markercluster": "^1.5.2"
  }
}
```