> **本文回答**：Marker 性能优化 —— 数量阈值、viewport 裁剪、降级到 Symbol 图层。

## 一、Marker 数量对性能的影响

| 数量 | 状态 | 建议 |
|---|---|---|
| < 200 | 流畅 | 任意使用 |
| 200–500 | 略卡 | `removeWhenNoInMapView: true` |
| 500–2000 | 明显卡 | cluster 或 Symbol layer |
| 2000+ | 严重卡顿 | 必须用 Symbol layer |

数据来源：一个 DOM Marker 是一个 div + CSS；每次 zoom/pan 都会重新定位（transform），几千个就 GC 频繁。

## 二、视野裁剪

```js
vjmap.createMarker({ removeWhenNoInMapView: true, removeWhenNoInMapViewPadding: 500 })
  .setLngLat([lng, lat]).addTo(map);
// removeWhenNoInMapView 是 vjmap 扩展字段：createMarker 内部调 setRemoveWhenNoInMapView；
// 写进 new Marker() 构造参数不生效
```

- 不在视野的 Marker 从 DOM 移除（真的移除，不是 display:none）
- 进入视野再重新插入 DOM
- 对 2000+ Marker 可以流畅到 500 以下

## 三、缩小自动缩放

```js
vjmap.createMarker({ scaleMaxZoom: 16 })  // 扩展字段，须走 createMarker
```

低 zoom 时 div 按比例缩小，减少像素绘制压力。

## 四、复用 DOM

一个 Marker 被 remove 后，下次 new 时 DOM 会重建。大数量场景推荐对象池：

```js
const pool = [];
function getMarker() {
  return pool.pop() || new vjmap.Marker();
}
function releaseMarker(m) {
  m.remove();
  pool.push(m);
}
```

## 五、不要在循环里做 map.project

```js
// 慢
for (const m of markers) {
  const p = map.project(m.getLngLat());
  ...
}

// 快：只在 zoomend / moveend 做
map.on("moveend", () => {
  for (const m of markers) {
    const p = map.project(m.getLngLat());
    ...
  }
});
```

## 六、降级到 Symbol 图层

DOM Marker 无法承受几万点；改用 vjmap Symbol 图层（WebGL）：

```js
map.addSource("pts", {
  type: "geojson",
  data: { type: "FeatureCollection", features: [...] }
});

await map.loadImageEx("pin", "/icons/pin.png");

map.addLayer({
  id: "pts-symbol",
  type: "symbol",
  source: "pts",
  layout: {
    "icon-image": "pin",
    "icon-size": 0.6,
    "icon-allow-overlap": true,
    "text-field": ["get", "name"],
    "text-size": 10,
    "text-offset": [0, 1.2],
    "text-anchor": "top"
  },
  paint: { "text-color": "#000", "text-halo-color": "#fff", "text-halo-width": 1 }
});
```

优点：几十万点依然 60 FPS。

## 七、Marker vs Symbol 选型

| 特性 | Marker (DOM) | Symbol (WebGL) |
|---|---|---|
| 数量 | < 500 | 任意 |
| 自定义 | 100% 随意 CSS/HTML | 受限（icon + text） |
| 动画 | CSS/JS 自由 | 只能 paint 插值 |
| 交互 | click/hover 原生 | `map.on("click", layerId, ...)` |
| 旋转倾斜 | 需设置 alignment | 原生支持 |
| 成本 | 高 | 极低 |

## 八、热力 / 聚合替代 Marker

几万点显示"热度"，用 heatmap 图层：

```js
map.addLayer({
  id: "heat",
  type: "heatmap",
  source: "pts",
  paint: {
    "heatmap-weight": 1,
    "heatmap-intensity": 1,
    "heatmap-radius": 30
  }
});
```

## 九、事件节流

```js
map.on("mousemove", throttle(e => {
  // 高频事件
}, 100));

function throttle(fn, ms) {
  let last = 0;
  return (...args) => {
    const now = Date.now();
    if (now - last > ms) { fn(...args); last = now; }
  };
}
```

## 十、常见坑

1. **一次性 `new vjmap.Marker` 5000 个** → 页面直接卡死。改为分批或用 Symbol。
2. **没 remove 旧 Marker** → 每次刷新累积，几次后就爆。
3. **每次 move 都 queryRenderedFeatures** → 100 ms 节流。
4. **用 DOM Marker 做轨迹动画** → 卡顿；轨迹只用一个 Marker + setLngLat。
5. **没用 `will-change: transform`** → 动画帧偶现闪烁。
