---
title: "MapboxLayer"
description: "声明式图层，按 type 渲染并经 paint / layout / filter 控制样式，支持内联 source 与图层交互事件。"
seo_title: "MapboxLayer Component"
seo_description: "MapboxLayer declaratively renders a mapbox-gl layer by type with paint / layout / filter, supports inline sources, beforeId ordering and per-layer interaction events."
canonical_url: "https://mapbox.mhaibaraai.cn/docs/core/layer"
---
# MapboxLayer

> 声明式图层，按 type 渲染并经 paint / layout / filter 控制样式，支持内联 source 与图层交互事件。

## 简介

`MapboxLayer` 是通用图层组件：`type` 选渲染方式，`paint` / `layout` / `filter` 控制样式，响应式变更走 `setPaintProperty` / `setLayoutProperty` / `setFilter` 增量下发，不重建图层。`source` 既可传 `sourceId` 引用 [MapboxSource](/docs/core/source)，也可传内联 source 对象（自动创建匿名源并随图层卸载）；`beforeId` 控制插入次序。

## 用法

同一内联多边形源派生 `fill` 与 `line` 两层，`beforeId` 让描边压在填充之上：

```vue [LayerBasicExample.vue]
<script setup lang="ts">
import type { FeatureCollection } from 'geojson'

const data: FeatureCollection = {
  type: 'FeatureCollection',
  features: [{
    type: 'Feature',
    properties: {},
    geometry: {
      type: 'Polygon',
      coordinates: [[[116.36, 39.95], [116.44, 39.95], [116.44, 39.89], [116.36, 39.89], [116.36, 39.95]]]
    }
  }]
}
</script>

<template>
  <div class="h-115 w-full overflow-hidden rounded-(--ui-radius) border border-default">
    <MapboxMap :options="{ style: 'mapbox://styles/mapbox/light-v11', center: [116.4, 39.92], zoom: 11 }">
      <!-- 同一内联源派生 fill 与 line 两层，beforeId 控制描边压在填充之上 -->
      <MapboxLayer
        layer-id="zone-fill"
        type="fill"
        :source="{ type: 'geojson', data }"
        :paint="{ 'fill-color': '#f43f5e', 'fill-opacity': 0.3 }"
      />
      <MapboxLayer
        layer-id="zone-line"
        type="line"
        :source="{ type: 'geojson', data }"
        :paint="{ 'line-color': '#f43f5e', 'line-width': 2 }"
      />
    </MapboxMap>
  </div>
</template>
```

## 示例

### 点选交互

监听图层 `@click` 取命中要素，从 `properties` 读取业务字段：

```vue [LayerInteractionExample.vue]
<script setup lang="ts">
import type { FeatureCollection } from 'geojson'
import type { MapEventOf } from 'mapbox-gl'

const data: FeatureCollection = {
  type: 'FeatureCollection',
  features: [
    { type: 'Feature', properties: { name: '天安门' }, geometry: { type: 'Point', coordinates: [116.397, 39.908] } },
    { type: 'Feature', properties: { name: '国贸' }, geometry: { type: 'Point', coordinates: [116.461, 39.909] } },
    { type: 'Feature', properties: { name: '中关村' }, geometry: { type: 'Point', coordinates: [116.316, 39.983] } }
  ]
}

const selected = ref<string | null>(null)

// 图层 click 事件携带命中要素，从 properties 读取业务字段
function onClick(event: MapEventOf<'click'>) {
  selected.value = (event.features?.[0]?.properties?.name as string) ?? null
}
</script>

<template>
  <div class="h-115 w-full overflow-hidden rounded-(--ui-radius) border border-default">
    <MapboxMap :options="{ style: 'mapbox://styles/mapbox/light-v11', center: [116.39, 39.93], zoom: 11 }">
      <MapboxLayer
        layer-id="poi"
        type="circle"
        :source="{ type: 'geojson', data }"
        :paint="{ 'circle-radius': 9, 'circle-color': '#f43f5e', 'circle-stroke-width': 2, 'circle-stroke-color': '#fff' }"
        @click="onClick"
      />
      <div class="absolute right-3 top-3 z-10 rounded-(--ui-radius) border border-default bg-default/80 px-3 py-1.5 text-sm backdrop-blur">
        {{ selected ? `已选中：${selected}` : '点击任意圆点' }}
      </div>
    </MapboxMap>
  </div>
</template>
```

### 过滤渲染

`filter` 传表达式，仅渲染匹配要素，切换即时生效：

```vue [LayerFilterExample.vue]
<script setup lang="ts">
import type { FeatureCollection } from 'geojson'
import type { FilterSpecification } from 'mapbox-gl'

const data: FeatureCollection = {
  type: 'FeatureCollection',
  features: Array.from({ length: 30 }, (_, i) => ({
    type: 'Feature',
    properties: { level: i % 3 },
    geometry: {
      type: 'Point',
      coordinates: [116.2 + ((i * 13) % 40) / 100, 39.8 + ((i * 29) % 25) / 100]
    }
  }))
}

const levels = [
  { label: 'All', value: -1 },
  { label: 'Level 0', value: 0 },
  { label: 'Level 1', value: 1 },
  { label: 'Level 2', value: 2 }
]
const active = ref(-1)

// filter 为表达式：仅渲染匹配要素，切换时即时生效（setFilter）
const filter = computed<FilterSpecification | undefined>(() =>
  active.value === -1 ? undefined : ['==', ['get', 'level'], active.value]
)
</script>

<template>
  <div class="h-115 w-full overflow-hidden rounded-(--ui-radius) border border-default">
    <MapboxMap :options="{ style: 'mapbox://styles/mapbox/light-v11', center: [116.4, 39.9], zoom: 10 }">
      <div class="absolute left-3 top-3 z-10 flex flex-wrap gap-2">
        <UButton
          v-for="lv in levels"
          :key="lv.value"
          size="xs"
          :color="active === lv.value ? 'primary' : 'neutral'"
          variant="solid"
          @click="active = lv.value"
        >
          {{ lv.label }}
        </UButton>
      </div>
      <MapboxLayer
        layer-id="points"
        type="circle"
        :source="{ type: 'geojson', data }"
        :filter="filter"
        :paint="{
          'circle-radius': 7,
          'circle-color': ['match', ['get', 'level'], 0, '#3b82f6', 1, '#f59e0b', 2, '#ef4444', '#888'],
          'circle-stroke-width': 1,
          'circle-stroke-color': '#fff'
        }"
      />
    </MapboxMap>
  </div>
</template>
```

## API

### Props

```ts
/**
 * Props for the MapboxLayer component
 */
interface MapboxLayerProps {
  /**
   * 图层 id，全局唯一
   */
  layerId: string;
  /**
   * 图层类型，决定渲染方式与可用的 paint / layout 属性
   */
  type: "symbol" | "fill" | "line" | "circle" | "heatmap" | "fill-extrusion" | "building" | "raster" | "raster-particle" | "hillshade" | "model" | "background" | "sky" | "slot" | "clip";
  /**
   * source id 字符串引用，或内联 source 对象（自动创建匿名源并随图层卸载）
   */
  source?: string | mapboxgl.SourceSpecification | undefined;
  /**
   * 矢量瓦片源内的子图层名（source-layer），消费矢量源时必填
   */
  sourceLayer?: string | undefined;
  /**
   * 绘制样式属性，响应式变更经 setPaintProperty 增量下发
   */
  paint?: PropBag | undefined;
  /**
   * 布局属性，响应式变更经 setLayoutProperty 增量下发
   */
  layout?: PropBag | undefined;
  /**
   * 过滤表达式，仅渲染匹配的要素
   */
  filter?: mapboxgl.FilterSpecification | undefined;
  /**
   * 最小缩放级别，低于此级别不渲染
   */
  minzoom?: number | undefined;
  /**
   * 最大缩放级别，高于此级别不渲染
   */
  maxzoom?: number | undefined;
  /**
   * 插入到该 id 图层之前；省略则追加到图层栈顶部
   */
  beforeId?: string | undefined;
}
```

### Emits

```ts
/**
 * Emitted events for the MapboxLayer component
 */
interface MapboxLayerEmits {
  click: (payload: [event: mapboxgl.MapMouseEvent]) => void;
  contextmenu: (payload: [event: mapboxgl.MapMouseEvent]) => void;
  dblclick: (payload: [event: mapboxgl.MapMouseEvent]) => void;
  mousedown: (payload: [event: mapboxgl.MapMouseEvent]) => void;
  mouseenter: (payload: [event: mapboxgl.MapMouseEvent]) => void;
  mouseleave: (payload: [event: mapboxgl.MapMouseEvent]) => void;
  mousemove: (payload: [event: mapboxgl.MapMouseEvent]) => void;
  mouseup: (payload: [event: mapboxgl.MapMouseEvent]) => void;
}
```

### Slots

```ts
/**
 * Slots for the MapboxLayer component
 */
interface MapboxLayerSlots {
  default(): any;
}
```

## Changelog

See commit history for [src/runtime/components/MapboxLayer.vue](https://github.com/mhaibaraai/movk-mapbox/commits/main/src/runtime/components/MapboxLayer.vue).


## Sitemap

See the full [sitemap](/sitemap.md) for all pages.
