Vitepress e leaflet.markercluster
Alessandro T.
Posted on May 21, 2024
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: 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:
<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) 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". Esistono diverse implementazioni possibili (ad es. async components) ma in questo caso preferisco utilizzare la classica sintassi javascript all'interno dell'hook onBeforeMount()
. Si può quindi eliminare il malfunzionamento in questo modo:
<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:
{
"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"
}
}
Posted on May 21, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.