关于 mapbox-web-integration-patterns
mapbox-web-integration-patterns 是指导在 Web 应用中集成 Mapbox GL JS 和 Mapbox Search JS 的 Agent 技能。内容覆盖多种前端框架,并着重说明生命周期管理、令牌处理、清理和常见集成错误。
主要功能
- 01React、Vue、Svelte 和 Angular 集成模式
- 02原生 JavaScript 与 Next.js 指南
- 03Mapbox Search JS 集成
- 04地图生命周期与清理实践
- 05令牌处理及常见错误检查
适用场景
- 01在 Web 应用中添加 Mapbox GL JS
- 02为前端框架构建地图组件
- 03集成 Mapbox Search JS
- 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.mdevals/evals.jsonreferences/angular.mdcommon-mistakes.mdnextjs.mdsvelte.mdtoken-management.mdvanilla.mdvue.mdweb-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 集成都必须:
- 在正确的生命周期钩子中初始化地图
- 将地图实例存储在组件状态中(不要在每次渲染时重新创建)
- 清理时始终调用
map.remove(),以防止内存泄漏 - 安全地管理 Token(使用环境变量)
- 导入 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 公共 Tokenmap:地图实例(必须先初始化)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 Routerreferences/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:常见地图样式模式