Appearance
Vue 3 组件(@geoverse/vue)
@geoverse/vue 是 GeoVerse 的官方 Vue 3 组件层:把 core 的命令式 API 包装成声明式组件树与命令式 composable,让你用模板和响应式数据驱动地图。它是一层薄绑定——不复制任何地图逻辑,坐标 / 聚合 / 绘制等全部留在 core。
React 用户见 React 组件(@geoverse/react),组件清单与事件映射约定与本包 1:1 对齐。
安装
shell
pnpm add @geoverse/vue geoverse olgeoverse、ol、vue 均为 peerDependencies(与宿主共享实例)。要素变换组件 <GvTransform> 额外依赖可选 peer ol-ext,用到时再装 pnpm add ol-ext。
快速上手(声明式)
vue
<script setup lang="ts">
import { ref } from 'vue';
import { GvMap, GvVectorLayer, GvMarker, GvInfoWindow } from '@geoverse/vue';
const center = ref([118.18, 24.49]);
const zoom = ref(12);
const open = ref(false);
</script>
<template>
<GvMap
style="height: 480px"
base="gd-vec"
v-model:center="center"
v-model:zoom="zoom"
scale-line
>
<GvVectorLayer>
<GvMarker
:options="{ position: [118.18, 24.49], color: 'red', size: 8 }"
@click="open = true"
/>
</GvVectorLayer>
<GvInfoWindow v-model:open="open" :position="[118.18, 24.49]">
插槽内容,点击标注弹出<!-- 气泡外观由内置默认皮肤提供,如需自定义覆盖 .geoverse-info-window -->
</GvInfoWindow>
</GvMap>
</template><GvMap>在onMounted内创建GMap(SSR 安全),就绪后才渲染子组件并provide给它们;center/zoom受控,支持v-model;底图base变化走switchBase;- 要素组件必须置于
<GvVectorLayer>内,否则抛出清晰错误。
命令式(composable)
需要完全控制时用 useMap(),在自己的容器上创建 / 销毁地图:
vue
<script setup lang="ts">
import { ref } from 'vue';
import { useMap } from '@geoverse/vue';
import { Marker } from '@geoverse/core-ol';
const el = ref<HTMLElement>();
const { map } = useMap(
{ base: 'gd-vec', center: [118.18, 24.49], zoom: 12 },
el,
);
function addPoint() {
// map.value 是 markRaw 的 GMap 实例,可直接调用任意 core API
map.value?.addLayer(/* ... */);
}
</script>
<template>
<div ref="el" style="height: 480px"></div>
</template>在 <GvMap> 子树内则用 injectMap() 取父地图。
全局注册(可选)
ts
import { createApp } from 'vue';
import { GeoVerseVue } from '@geoverse/vue';
createApp(App).use(GeoVerseVue); // 批量注册全部组件也可只具名导入用到的组件,无需安装插件(利于 tree-shaking)。
组件一览
| 类别 | 组件 |
|---|---|
| 容器 | GvMap |
| 图层 | GvVectorLayer、GvHeatLayer、GvImageLayer、GvCustomBaseLayer、GvTrafficLayer、GvClusterLayer、GvSuperClusterLayer、GvMassLayer、GvNameLayer |
| 要素 | GvMarker、GvCircle、GvPolygon、GvPolyline |
| 交互 | GvDraw、GvMeasure、GvFeatureEditor、GvTransform(@experimental) |
| 控件 / 弹窗 | GvOverview、GvInfoWindow |
| 轨迹 | GvPathSimplifier |
约定:图层 / 要素 / 工具组件的 options prop = 对应 core 类的构造选项;map / view 由组件自动注入;事件经 emits 转发(如要素的 @click、绘制的 @complete、轨迹的 @move)。每个组件用 defineExpose 暴露底层实例(模板 ref 可取)。
受控属性与响应式更新
<GvMap>的center/zoom双向受控,支持v-model;底图base变化触发switchBase(切投影并重投影既有图层/要素)。- 要素几何受控:
<GvMarker :options>的position、<GvCircle>的center、<GvPolygon>/<GvPolyline>的path变化时,组件会按当前底图投影自动重投影并更新几何。坐标统一以 WGS-84 经纬度传入。- 几何更新按「值」比较触发(而非对象引用),因此即使用内联
:options="{ ... }"(每次渲染新对象),切底图等无关重渲染也不会误重置几何。
- 几何更新按「值」比较触发(而非对象引用),因此即使用内联
- 其余「构造即生效」的选项(如样式、聚合
distance)变化时不自动响应,需要时可经组件ref取实例命令式更新,或重建组件。
注意事项
- 响应式陷阱:组件内部所有 ol 派生实例(地图 / 图层 / 要素)一律
markRaw/shallowRef,绝不进入深层响应式——这是正确性前提。你自己持有这些实例时也请遵循。 - SSR:所有实例化都在
onMounted,Nuxt 3 服务端构建安全;<GvMap>渲染期只产出容器 DOM。 - 挂载顺序:图层组件在
setup阶段即把图层加入地图(早于子要素挂载),确保子要素addFeature时图层投影已同步、坐标不错位。 - 卸载:子组件先移除自身(
removeFeature/removeLayer),地图组件最后destroy();定时器 / 监听 / overlay 由 core 的destroy()完整回收。
属性操作(编辑 / 过滤 / 定位)
配合编辑器三包(@geoverse/editor-core + @geoverse/editor-ol),@geoverse/vue 另提供一组headless composable 与无样式参考组件(RFC-0007)。属性编辑可撤销可同步、过滤高亮、按 id 定位,全部围绕一个 EditSession 展开。
Headless composables(零样式,自己渲染 UI):
| composable | 作用 |
|---|---|
useFieldSchema | 字段表(声明 ∪ 推断),随编辑实时重算 |
useFeatureAttributes | 单要素草稿副本 + setField/save/reset(save 走可撤销命令) |
useAttributeFilter | 谓词过滤状态 + matchedIds + apply/clear |
useFeatureLocate | locate(ids) → core extentOf → 适配器 fit |
无样式参考组件(功能完整、可整体替换):GvAttributeForm / GvAttributeFilter / GvFeatureList,默认插槽暴露内部状态以接管渲染。
vue
<script setup lang="ts">
import { ref, shallowRef } from 'vue';
import { GvMap, GvAttributeForm, GvAttributeFilter, GvFeatureList } from '@geoverse/vue';
import type { GMap } from '@geoverse/core-ol';
import { EditSession } from '@geoverse/editor-core';
import { OlEditorAdapter } from '@geoverse/editor-ol';
const session = shallowRef<EditSession>();
const adapter = shallowRef<OlEditorAdapter>();
const selectedId = ref<string | null>(null);
function onReady(map: GMap) {
const crs = map.getView().getProjection().getCode();
const s = new EditSession({ crs, features: /* … */ });
const a = new OlEditorAdapter(map, {
crs,
onSelect: (id, additive) => {
if (id) s.select([id], additive);
else if (!additive) s.clearSelection();
a.setSelection(s.getSelected());
selectedId.value = id;
},
});
s.attach(a);
// 过滤命中 → 适配器重着色(命中高亮、其余淡出)
s.onFilterChange(({ matchedIds, scopedIds }) =>
a.applyFilter(matchedIds.length ? matchedIds : null, scopedIds),
);
session.value = s;
adapter.value = a;
}
</script>
<template>
<GvMap base="gd-vec" :center="[118.1, 24.55]" :zoom="11" @ready="onReady" />
<template v-if="session">
<GvAttributeFilter :session="session" />
<GvFeatureList :session="session" :locator="adapter" />
<GvAttributeForm :session="session" :feature-id="selectedId" />
</template>
</template>关于 session 与响应式
EditSession / 适配器等编辑实例同样不进深层响应式(用 shallowRef 持有、markRaw 心智),与 ol 实例一致。详见编辑器 · 属性操作。
MapLibre 引擎(@geoverse/vue/libre 子路径)
根入口 @geoverse/vue 绑定 OpenLayers 引擎(geoverse/ol)。若要用 MapLibre GL JS 渲染,改从子路径 @geoverse/vue/libre 导入——它绑定 geoverse/libre,且不会把 OpenLayers 打进 bundle。两套子路径心智一致(同名组件、options = 对应 core 类构造项、WGS-84 坐标),差异见下。
vue
<script setup lang="ts">
import { ref } from 'vue';
import {
GvMap,
GvVectorLayer,
GvMarker,
GvEditSession,
} from '@geoverse/vue/libre';
const base = ref<'osm' | 'gd-vec' | 'tiandi-vec'>('osm');
const center = ref<[number, number]>([118.18, 24.49]);
const zoom = ref(11);
</script>
<template>
<GvMap :base="base" v-model:center="center" v-model:zoom="zoom">
<GvVectorLayer>
<GvMarker :options="{ position: [118.18, 24.49], color: 'red' }" />
</GvVectorLayer>
</GvMap>
</template>与 OL 版的差异:
- 组件是 core-libre 能力的子集:仅
GvMap、GvVectorLayer、GvVectorTileLayer、GvMarker、GvCircle、GvPolygon、GvPolyline、GvInfoWindow、GvEditSession。没有GvHeatLayer/GvMassLayer/GvClusterLayer/GvTrafficLayer/GvImageLayer/GvCustomBaseLayer、GvMeasure/GvDraw/GvTransform、GvOverview/GvPathSimplifier/GvNameLayer(core-libre 无对应能力)。 - 就绪门控:
<GvMap>在 MapLibre style 加载完成(whenReady)后才渲染子树并触发ready,子组件挂载时可安全建源 / 加层。 - 无重投影:libre 要素坐标恒为 WGS-84,直接存储,切底图不漂移(数据边界变换 + 百度瓦片纠偏由 core-libre 处理)。
- 事件命名随 MapLibre:
<GvMap>派发click/mousemove/moveend(非 OL 的singleclick/pointermove)。 - 样式:请引入
maplibre-gl/dist/maplibre-gl.css(弹窗等 UI 依赖)。
编辑器:libre 没有 core 内建交互,<GvEditSession> 走框架无关编辑引擎 + 自绘适配器(EditSession + LibreEditorAdapter),支持撤销/重做/打断/合并/拓扑等命令,并把会话 provide 给子树——同一套无样式属性/历史组合式与参考组件(useFeatureAttributes、GvAttributeForm、GvHistoryPanel 等)在 libre 子路径同样可用(本就引擎无关)。
vue
<template>
<GvMap base="osm" :center="[118.15, 24.5]" :zoom="12">
<GvEditSession :features="seed" :mode="mode" @snapshot="onSnapshot" />
</GvMap>
</template>
peerDependencies按引擎可选:只用 libre 时安装@geoverse/core-libre+maplibre-gl(编辑再加@geoverse/editor-libre),无需安装ol/@geoverse/core-ol。
在线示例:仓库
examples/vue/(pnpm dev:examples→/vue/基础组件、/vue/attributes.html属性操作)。
