Appearance
v-map mit Nuxt 4
Dieser Guide zeigt, wie du v-map in eine Nuxt-4-App einbindest. Die Live-Demo unten ist die echte App aus examples/nuxt/ des v-map-Repos, gebaut mit nuxt generate (Nitro static-Preset) und in einem sandboxed Iframe eingebettet.
Live-Demo
Live demo (sandboxed Iframe): nuxt/ · in neuem Tab öffnen
Was die Demo zeigt:
- Reactive Provider-Switch: Dropdown ist
v-model-gebunden an eineref<'ol' | 'leaflet' | 'deck' | 'cesium'>(), die als:flavour-Prop ans<v-map>-Element fließt — die Karte schaltet zwischen den Providern um, ohne Re-Mount. - Reactive Zoom-Slider: Slider mit
v-model.numberschreibt in eineref<number>(), die als:zoomans<v-map>gebunden ist. Der@Watch('zoom')-Handler in v-map propagiert die Änderung an den aktiven Provider und behält den aktuellen Center bei. - Layer-Toggle: GeoTIFF- und GeoJSON-Buttons mutieren
ref()s, Vue rendert die Layer-Komponenten perv-ifein und aus. <v-map-error>Toast: deklarativer Fehler-Stapel unten rechts, ohne JavaScript-Listener-Code.- Programmatischer
vmap-error-Listener:@vmap-errordirekt am<v-map>-Element — typisches Vue-Custom-Event-Handling, weil Stencil-Events auch hier nativ bubbeln. - „Add broken WMS layer" Button: triggert absichtlich einen Lade-Fehler, um die End-to-End Error-API live zu sehen.
Setup für eigene Projekte
1. Projekt anlegen
bash
pnpm create nuxt@latest my-vmap-app
cd my-vmap-app
pnpm installNuxt 4 nutzt das neue app/-Verzeichnis für Pages und Components, und legt nuxt.config.ts ins Projekt-Root.
2. v-map installieren
bash
pnpm add @npm9912/v-mapDie Provider (ol, leaflet, cesium, @deck.gl/*) brauchst du nur als Dev-Dependencies für TypeScript-Typen, falls überhaupt — zur Laufzeit lädt v-map seine Peer-Deps via Auto-Importmap nach.
3. Vue als Custom-Element-tolerant konfigurieren
Vue's Template-Compiler versucht standardmäßig, jeden Tag als Vue-Komponente zu resolven. Damit <v-map> und seine Layer-Kinder als native Custom Elements behandelt werden, braucht es eine Konfigurations- Zeile in nuxt.config.ts:
ts
// nuxt.config.ts
export default defineNuxtConfig({
vue: {
compilerOptions: {
isCustomElement: tag => tag === 'v-map' || tag.startsWith('v-map-'),
},
},
});Ohne diesen Switch logged Nuxt bei jedem Render eine Warnung: [Vue warn]: Failed to resolve component: v-map.
4. v-map-Loader im <head> einbinden
Wir empfehlen, v-map per <script type="module"> aus jsDelivr zu laden, statt den Stencil-Loader durch Nitros Vite-Build zu schleifen. Der sauberste Weg in Nuxt ist useHead:
vue
<!-- app/pages/index.vue -->
<script setup lang="ts">
useHead({
title: 'My v-map App',
script: [
{
src: 'https://cdn.jsdelivr.net/npm/@npm9912/v-map@0.5.0/dist/v-map/v-map.esm.js',
type: 'module',
},
],
});
</script>Warum CDN statt Bundler
Stencils Lazy-Loader nutzt import.meta.url, um seine Sibling-*.entry.js- Chunks zur Laufzeit zu finden. Wenn ein Bundler (Vite, Webpack, Nitro) den Loader ingestet, landet die URL bei /_nuxt/abc.js und Stencil 404t auf jedem einzelnen Layer-Chunk. Der jsDelivr-Pfad lässt Stencils Chunk-Resolution unverändert. Siehe den CDN-Guide für die Hintergründe.
5. Erste Karte deklarativ
vue
<!-- app/pages/index.vue -->
<script setup lang="ts">
const zoom = ref(11);
useHead({
script: [
{
src: 'https://cdn.jsdelivr.net/npm/@npm9912/v-map@0.5.0/dist/v-map/v-map.esm.js',
type: 'module',
},
],
});
</script>
<template>
<v-map flavour="ol" center="11.576,48.137" :zoom="zoom">
<v-map-error position="bottom-right" auto-dismiss="6000"></v-map-error>
<v-map-layergroup group-title="Base" basemapid="osm">
<v-map-layer-osm id="osm" label="OpenStreetMap"></v-map-layer-osm>
</v-map-layergroup>
</v-map>
<input v-model.number="zoom" type="range" min="2" max="18" />
</template>
<style>
v-map {
display: block;
width: 100%;
height: 70vh;
}
</style>Drag den Slider — die Karte zoomt direkt mit. Vue erkennt die geänderte zoom-Prop, der @Watch('zoom')-Handler in v-map (seit v0.4.0) ruft den Provider mit dem aktuellen Center neu auf.
6. Reactive Layer hinzufügen oder ausblenden
Layer können per v-if bedingt im DOM stehen. v-map disposed den jeweiligen Layer beim Verbergen automatisch:
vue
<script setup lang="ts">
const showOverlay = ref(false);
</script>
<template>
<button @click="showOverlay = !showOverlay">Toggle Overlay</button>
<v-map flavour="ol">
<v-map-layergroup group-title="Base" basemapid="osm">
<v-map-layer-osm id="osm"></v-map-layer-osm>
</v-map-layergroup>
<v-map-layergroup v-if="showOverlay" group-title="Daten">
<v-map-layer-geojson url="/data/points.geojson"></v-map-layer-geojson>
</v-map-layergroup>
</v-map>
</template>7. Error-Events programmatisch konsumieren
vmap-error bubblet von jedem Layer zu <v-map> hoch. Vue's @- Syntax bindet auf Custom-Events direkt — kein addEventListener-Boilerplate nötig:
vue
<script setup lang="ts">
type VMapErrorDetail = {
type: 'network' | 'validation' | 'parse' | 'provider';
message: string;
attribute?: string;
cause?: unknown;
};
function onMapError(event: Event) {
const detail = (event as CustomEvent<VMapErrorDetail>).detail;
if (!detail) return;
console.error('[vmap]', detail.type, detail.message);
// hier z. B. in einen Pinia-Store pushen
}
</script>
<template>
<v-map flavour="ol" @vmap-error="onMapError">
<v-map-error position="bottom-right" auto-dismiss="6000"></v-map-error>
...
</v-map>
</template>Für reines Toast-Display ohne JS-Listener reicht aber das deklarative <v-map-error>.
Statisches Generieren
Nuxt 4 produziert mit nuxt generate ein vollständig statisches .output/public/-Verzeichnis (Nitro static-Preset), das du auf jedem File-Server hosten kannst:
bash
pnpm generateWenn du unter einem Subpfad deployst (z. B. GitHub Pages):
ts
// nuxt.config.ts
export default defineNuxtConfig({
app: {
baseURL: process.env.BASE_PATH ?? '/my-vmap-app/',
},
});Stolperfallen
isCustomElementvergessen. Ohne den Compiler-Switch logged Vue bei jedem Render Warnungen und kann (je nach Vue-Version) sogar versuchen,<v-map>als Komponente zu mounten. Immer innuxt.config.tssetzen, nicht erst per Plugin.process.client-Guards überflüssig. Du brauchst keine<ClientOnly>-Wrapper um<v-map>. Nuxt prerendert die Page-Shell inklusive<v-map>-Markup, weil das ja nur statisches HTML ist — die Custom-Element-Upgrade-Logik startet erst, wenn der jsDelivr- Loader im Browser ausgeführt wird. Zur SSR-Zeit ist<v-map>ein einfacher unbekannter Tag.nuxt buildvsnuxt generate.buildproduziert einen Server-Bundle (Node/edge),generateeinen statischen Export. Für eine v-map-only Single-Page-App willst du fast immergenerate, weil die ganze Logik client-side ist und kein Backend gebraucht wird.Vite optimizeDeps verschluckt v-map. Wenn beim Dev-Server v-map Chunks fehlen, in
nuxt.config.ts:tsvite: { optimizeDeps: { exclude: ['@npm9912/v-map'], }, }v-map ist ESM und braucht kein Pre-Bundling.
Vollständiger Code
Den kompletten Source der Live-Demo oben findest du im v-map-Repo unter examples/nuxt/. Der relevante Teil ist app/pages/index.vue.
Siehe auch
- Vue 3 — die Standalone-Vue-Variante (ohne Nuxt)
- Getting Started — generelles Setup
- CDN ohne Bundler — die jsDelivr-Lade-Strategie im Detail
- Error Handling — die
<v-map-error>Komponente und dasvmap-errorEvent - Komponenten-API — alle 19 Web Components