mcpField
M
mapbox-web-integration-patterns

mapbox-web-integration-patterns

github.com/mapbox开发832.6k 次安装4 周前更新
代码仓库

关于 mapbox-web-integration-patterns

mapbox-web-integration-patterns 是指导在 Web 应用中集成 Mapbox GL JS 和 Mapbox Search JS 的 Agent 技能。内容覆盖多种前端框架,并着重说明生命周期管理、令牌处理、清理和常见集成错误。

主要功能

  1. 01React、Vue、Svelte 和 Angular 集成模式
  2. 02原生 JavaScript 与 Next.js 指南
  3. 03Mapbox Search JS 集成
  4. 04地图生命周期与清理实践
  5. 05令牌处理及常见错误检查

适用场景

  1. 01在 Web 应用中添加 Mapbox GL JS
  2. 02为前端框架构建地图组件
  3. 03集成 Mapbox Search JS
  4. 04排查地图生命周期或样式切换问题

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/mapbox/mapbox-agent-skills --skill mapbox-web-integration-patterns

安装后在 Claude Code 中输入/mapbox-web-integration-patterns确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

AGENTS.md
evals/
evals.json
references/
angular.md
common-mistakes.md
nextjs.md
svelte.md
token-management.md
vanilla.md
vue.md
web-components.md

详细介绍

Mapbox 集成模式技能

此技能提供了在 Web 应用中使用 React、Vue、Svelte、Angular 和原生 JavaScript 集成 Mapbox GL JS 的官方模式。这些模式基于 Mapbox 的 create-web-app 脚手架工具,代表了适用于生产环境的最佳实践。

版本要求

Mapbox GL JS

推荐: v3.x(最新版本)

  • 最低要求: v3.0.0
  • 选择 v3.x 的原因: 采用现代 API、性能更佳、持续开发
  • v2.x: 旧版本;不再积极开发(参见下方的迁移说明)

通过 npm 安装(推荐用于生产环境):

npm install mapbox-gl@^3.0.0    # Installs latest v3.x

CDN(仅用于原型开发):


<script src="https://api.mapbox.com/mapbox-gl-js/vVERSION/mapbox-gl.js"></script>
<link href="https://api.mapbox.com/mapbox-gl-js/vVERSION/mapbox-gl.css" rel="stylesheet" />
框架要求

React: GL JS 可用于 React 16.8+(需要 hooks)。create-web-app 使用 React 19.x 搭建脚手架。 Vue: GL JS 可用于 Vue 2.x+(推荐使用 Vue 3 Composition API)。 Svelte: GL JS 可用于任何版本的 Svelte。create-web-app 使用 Svelte 5.x 搭建脚手架。 Angular: GL JS 可用于 Angular 2+。create-web-app 使用 Angular 19.x 搭建脚手架。 Next.js: 最低版本为 13.x(App Router),Pages Router 需 12.x+。

Mapbox Search JS
npm install @mapbox/search-js-react@^1.0.0      # React
npm install @mapbox/search-js-web@^1.0.0        # Other frameworks
版本迁移说明(v2.x 到 v3.x)
  • 现在需要 WebGL 2
  • 已移除 optimizeForTerrain 选项
  • 改进了 TypeScript 类型,并更好地支持 tree-shaking
  • 核心初始化模式没有重大更改

Token 模式(适用于 v2.x 和 v3.x):

const token = import.meta.env.VITE_MAPBOX_ACCESS_TOKEN; // Use env vars in production

// Global token (works since v1.x)
mapboxgl.accessToken = token;
const map = new mapboxgl.Map({ container: '...' });

// Per-map token (preferred for multi-map setups)
const map = new mapboxgl.Map({
  accessToken: token,
  container: '...'
});

核心原则

每个 Mapbox GL JS 集成都必须:

  1. 在正确的生命周期钩子中初始化地图
  2. 将地图实例存储在组件状态中(不要在每次渲染时重新创建)
  3. 清理时始终调用 map.remove(),以防止内存泄漏
  4. 安全地管理 Token(使用环境变量)
  5. 导入 CSS:import 'mapbox-gl/dist/mapbox-gl.css'

React 集成(主要模式)

模式:使用 useRef + useEffect 并进行清理

注意: 这些示例使用 Vite(create-web-app 使用的打包器)。如果使用 Create React App,请将 import.meta.env.VITE_MAPBOX_ACCESS_TOKEN 替换为 process.env.REACT_APP_MAPBOX_TOKEN。其他打包器请参见 Token 管理模式。

import { useRef, useEffect } from 'react';
import mapboxgl from 'mapbox-gl';
import 'mapbox-gl/dist/mapbox-gl.css';

function MapComponent() {
  const mapRef = useRef(null); // Store map instance
  const mapContainerRef = useRef(null); // Store DOM reference

  useEffect(() => {
    mapboxgl.accessToken = import.meta.env.VITE_MAPBOX_ACCESS_TOKEN;

    mapRef.current = new mapboxgl.Map({
      container: mapContainerRef.current,
      center: [-71.05953, 42.3629],
      zoom: 13
    });

    // CRITICAL: Cleanup to prevent memory leaks
    return () => {
      mapRef.current.remove();
    };
  }, []); // Empty dependency array = run once on mount

  return <div ref={mapContainerRef} style={{ height: '100vh' }} />;
}

要点:

  • 地图实例和容器都使用 useRef
  • 在 useEffect 中初始化,并使用空依赖项 []
  • 始终返回会调用 map.remove() 的清理函数
  • 不要在渲染过程中初始化地图(会导致无限循环)
React + Search JS
import { useRef, useEffect, useState } from 'react';
import mapboxgl from 'mapbox-gl';
import { SearchBox } from '@mapbox/search-js-react';
import 'mapbox-gl/dist/mapbox-gl.css';

const accessToken = import.meta.env.VITE_MAPBOX_ACCESS_TOKEN;
const center = [-71.05953, 42.3629];

function MapWithSearch() {
  const mapRef = useRef(null);
  const mapContainerRef = useRef(null);
  const [inputValue, setInputValue] = useState('');

  useEffect(() => {
    mapboxgl.accessToken = accessToken;

    mapRef.current = new mapboxgl.Map({
      container: mapContainerRef.current,
      center: center,
      zoom: 13
    });

    return () => {
      mapRef.current.remove();
    };
  }, []);

  return (
    <>
      <div
        style={{
          margin: '10px 10px 0 0',
          width: 300,
          right: 0,
          top: 0,
          position: 'absolute',
          zIndex: 10
        }}
      >
        <SearchBox
          accessToken={accessToken}
          map={mapRef.current}
          mapboxgl={mapboxgl}
          value={inputValue}
          proximity={center}
          onChange={(d) => setInputValue(d)}
          marker
        />
      </div>
      <div ref={mapContainerRef} style={{ height: '100vh' }} />
    </>
  );
}

Search JS 集成摘要

安装:

npm install @mapbox/search-js-react      # React
npm install @mapbox/search-js-web        # Vanilla/Vue/Svelte

这两个软件包都将 @mapbox/search-js-core 作为依赖项。只有在构建自定义搜索 UI 时才需要直接安装 -core。

主要配置选项:

  • accessToken:你的 Mapbox 公共 Token
  • map:地图实例(必须先初始化)
  • mapboxgl:mapboxgl 库引用
  • proximity:[lng, lat],用于按地理位置调整结果优先级
  • marker:布尔值,用于显示或隐藏结果标记
  • placeholder:搜索框占位文本
搜索框定位

绝对定位(叠加显示):

<div
  style={{
    position: 'absolute',
    top: 10,
    right: 10,
    zIndex: 10,
    width: 300
  }}
>
  <SearchBox {...props} />
</div>

常见位置:

  • 右上角:top: 10px, right: 10px
  • 左上角:top: 10px, left: 10px
  • 左下角:bottom: 10px, left: 10px

常见错误(重要)

错误 1:忘记调用 map.remove()
// BAD - Memory leak!
useEffect(() => {
  const map = new mapboxgl.Map({ ... })
  // No cleanup function
}, [])

// GOOD - Proper cleanup
useEffect(() => {
  const map = new mapboxgl.Map({ ... })
  return () => map.remove()  // Cleanup
}, [])

原因: 每个地图实例都会创建 WebGL 上下文、事件监听器和 DOM 节点。如果不进行清理,这些内容会不断累积并导致内存泄漏。

错误 2:在渲染过程中初始化地图
// BAD - Infinite loop in React!
function MapComponent() {
  const map = new mapboxgl.Map({ ... })  // Runs on every render
  return <div />
}

// GOOD - Initialize in effect
function MapComponent() {
  useEffect(() => {
    const map = new mapboxgl.Map({ ... })
  }, [])
  return <div />
}

原因: React 组件会频繁重新渲染。每次渲染都创建新地图会导致无限循环和崩溃。

错误 3:未正确存储地图实例
// BAD - map variable lost between renders
function MapComponent() {
  useEffect(() => {
    let map = new mapboxgl.Map({ ... })
    // map variable is not accessible later
  }, [])
}

// GOOD - Store in useRef
function MapComponent() {
  const mapRef = useRef()
  useEffect(() => {
    mapRef.current = new mapboxgl.Map({ ... })
    // mapRef.current accessible throughout component
  }, [])
}

原因: 添加图层、标记或调用 remove() 等操作都需要访问地图实例。

错误 4:将地图实例存储在 Vue 的 data() 中(Vue 专属)
// BAD - Vue's reactivity wraps data() objects in a Proxy, breaking mapbox-gl internals!
export default {
  data() {
    return {
      map: null  // Will be wrapped in a Proxy
    }
  },
  mounted() {
    this.map = new mapboxgl.Map({ ... })  // Proxy breaks GL internals
  }
}

// GOOD - Assign map as a plain instance property, not in data()
export default {
  mounted() {
    this.map = new mapboxgl.Map({
      container: this.$refs.mapContainer,
      center: [-71.05953, 42.3629],
      zoom: 13
    })
  },
  unmounted() {
    this.map?.remove()
  }
}

原因: 在 Vue(尤其是 Vue 3)中,data() 属性会被包装在 Proxy 中以实现响应式。Mapbox GL JS 内部会检查对象标识,并使用无法在代理包装后保留的属性。将地图存储在 data() 中会导致隐蔽且难以调试的问题。应改为在 mounted() 中直接将地图实例赋值给 this.map——在 data() 之外赋值的属性不会变成响应式。

错误 5:样式/瓦片错误不易察觉(没有错误处理器)
// BAD — blank map when the token/style fails
const map = new mapboxgl.Map({ ... });

// GOOD — surface failures
map.on('error', (e) => {
  console.error(e.error || e);
  // optionally show an on-page error message
});
错误 6:通过 jsDelivr +esm 加载的 deck.gl CDN 损坏

<script type="module">
  import { MapboxOverlay } from 'https://cdn.jsdelivr.net/npm/@deck.gl/[email protected]/+esm';
</script>


<script src="https://unpkg.com/[email protected]/dist.min.js"></script>
<script>
  const { MapboxOverlay, ScatterplotLayer } = deck;
  map.addControl(
    new MapboxOverlay({
      interleaved: false,
      layers: [
        /* ... */
      ]
    })
  );
</script>

使用 MapboxOverlay(Mapbox IControl),不要将裸 Deck 用作地图控件。

错误 7:绘制工具栏缺少 draw.create

如果加载了 mapbox-gl-draw,请监听 draw.create(并使用 draw.getAll() 更新 UI)。未完全删除的处理器留下一个悬空的 });,会导致页面崩溃。

错误 8:调用 setStyle 后图层丢失(未在 style.load 时重新绑定)

map.setStyle(...) 会替换样式树。除非重新添加,否则之前添加的自定义源/图层/处理器都会被清除。

function onStyleReady() {
  // re-add sources, layers, and interaction handlers here
}

map.on('style.load', onStyleReady);

document.querySelectorAll('[data-style]').forEach((btn) => {
  btn.addEventListener('click', () => {
    map.setStyle(btn.dataset.style);
    // do NOT only add layers on the first 'load' — wait for style.load after every switch
  });
});

Agent 反模式: 样式切换按钮调用一次 setStyle,却没有在 style.load 时重新绑定。第一种样式能正常工作;之后每次切换都会出问题。

参考文件

加载以下文件,了解特定框架的模式和更多详细信息:

  • references/vue.md — Vue 集成(mounted/unmounted 生命周期)
  • references/svelte.md — Svelte 集成(onMount/onDestroy)
  • references/angular.md — Angular 集成与 SSR 处理
  • references/vanilla.md — Vanilla JS(Vite)+ Vanilla JS(CDN)
  • references/web-components.md — Web Components(基础 + 响应式 + 在 React/Vue/Svelte 中使用)
  • references/nextjs.md — Next.js App Router + Pages Router
  • references/common-mistakes.md — 常见错误 4-7 + 测试模式
  • references/token-management.md — 各打包工具的令牌管理 + 样式配置

何时使用此技能

在以下情况下调用此技能:

  • 在新项目中设置 Mapbox GL JS
  • 将 Mapbox 集成到特定框架中(React、Vue、Svelte、Angular、Next.js)
  • 构建与框架无关的 Web Components
  • 为组件库创建可复用的地图组件
  • 调试地图初始化问题
  • 添加 Mapbox Search 功能
  • 实现恰当的清理和生命周期管理
  • 在不同框架之间转换(例如,从 React 转到 Vue)
  • 检查代码是否遵循 Mapbox 集成最佳实践

相关技能

  • mapbox-cartography:地图设计原则和样式
  • mapbox-token-security:令牌管理和安全性
  • mapbox-style-patterns:常见地图样式模式

资源

评论

支持 Markdown