react-map-gl渲染上万Polygon:单Feature绑定Source致浏览器崩溃求解
解决方案
核心问题分析
你现在的问题是创建了10000个独立的Source和Layer组件,每个组件都会占用浏览器内存和地图渲染资源,直接导致浏览器崩溃。正确的做法是用单个Source加载所有Feature,通过地图的数据驱动样式来实现选中/hover的差异化样式,完全不需要拆分单个Feature。
具体实现步骤
- 统一加载FeatureCollection:把所有Polygon放到一个FeatureCollection里,作为单个Source的数据源。
- 数据驱动样式控制:利用Mapbox GL的表达式语法(比如
match、case),根据当前选中/hover的要素ID,动态切换填充和线样式。 - 事件处理优化:通过地图点击/鼠标移动事件,获取当前交互的要素ID,更新状态即可自动触发样式变化。
完整代码示例
import { useState, useRef } from 'react'; import { MapGL, Source, Layer, NavigationControl } from '@urbica/react-map-gl'; import 'mapbox-gl/dist/mapbox-gl.css'; const YourMapComponent = () => { const mapRef = useRef(null); const [viewport, setViewport] = useState({ width: '100%', height: '100vh', latitude: 39.8283, longitude: -98.5795, zoom: 4 }); const [selectedId, setSelectedId] = useState(null); const [hoveredId, setHoveredId] = useState(null); // 把单个Feature数组整合成FeatureCollection const featureCollection = { type: 'FeatureCollection', features: plantLocations }; const _handleViewport = (newViewport) => setViewport(newViewport); const handleMapClick = (event) => { // 获取点击位置的目标要素 const features = mapRef.current.queryRenderedFeatures(event.point, { layers: ['polygon-fill-layer'] }); setSelectedId(features.length > 0 ? features[0].properties.id : null); }; const handleMapMouseMove = (event) => { // 获取鼠标悬停的目标要素 const features = mapRef.current.queryRenderedFeatures(event.point, { layers: ['polygon-fill-layer'] }); if (features.length > 0) { setHoveredId(features[0].properties.id); mapRef.current.getCanvas().style.cursor = 'pointer'; } else { setHoveredId(null); mapRef.current.getCanvas().style.cursor = ''; } }; return ( <MapGL ref={mapRef} {...viewport} onViewStateChange={_handleViewport} onClick={handleMapClick} onMouseMove={handleMapMouseMove} mapStyle="mapbox://styles/mapbox/light-v10" accessToken="YOUR_MAPBOX_TOKEN" > {/* 单个Source加载所有Polygon */} <Source type="geojson" data={featureCollection} id="polygon-source"> {/* 主填充Layer:根据交互状态动态切换样式 */} <Layer id="polygon-fill-layer" type="fill" paint={{ 'fill-color': [ 'case', ['==', ['get', 'id'], selectedId], 'darkblue', ['==', ['get', 'id'], hoveredId], 'lightblue', 'blue' ], 'fill-opacity': [ 'case', ['==', ['get', 'id'], selectedId], 0.8, ['==', ['get', 'id'], hoveredId], 0.7, 0.6 ] }} /> {/* 选中/hover时显示的线Layer:仅匹配目标ID时可见 */} <Layer id="polygon-line-layer" type="line" paint={{ 'line-color': '#007A49', 'line-width': 4, 'line-opacity': [ 'case', ['||', ['==', ['get', 'id'], selectedId], ['==', ['get', 'id'], hoveredId]], 1, 0 ] }} /> </Source> <NavigationControl position="top-right" /> </MapGL> ); }; export default YourMapComponent;
关键优化点说明
- 单个Source:避免创建大量独立资源,大幅降低内存占用和渲染开销,10000个要素用单个Source完全可以流畅运行。
- 数据驱动样式:通过Mapbox GL的表达式语法直接在Layer属性中做条件判断,不需要动态创建/销毁组件。
- 事件处理:用
queryRenderedFeatures获取交互要素,比遍历所有Feature高效得多。 - 额外性能优化:如果要素数量过大仍有卡顿,可以给Source添加参数简化几何图形:
<Source type="geojson" data={featureCollection} id="polygon-source" maxzoom={14} // 超过14级才显示完整几何,低层级自动简化 simplify={0.5} // 几何简化阈值,单位为像素 />
内容的提问来源于stack exchange,提问作者Shreyas Chorge
相关产品推荐
相关产品推荐

