You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

react-map-gl渲染上万Polygon:单Feature绑定Source致浏览器崩溃求解

解决方案

核心问题分析

你现在的问题是创建了10000个独立的Source和Layer组件,每个组件都会占用浏览器内存和地图渲染资源,直接导致浏览器崩溃。正确的做法是用单个Source加载所有Feature,通过地图的数据驱动样式来实现选中/hover的差异化样式,完全不需要拆分单个Feature。

具体实现步骤

  1. 统一加载FeatureCollection:把所有Polygon放到一个FeatureCollection里,作为单个Source的数据源。
  2. 数据驱动样式控制:利用Mapbox GL的表达式语法(比如match、case),根据当前选中/hover的要素ID,动态切换填充和线样式。
  3. 事件处理优化:通过地图点击/鼠标移动事件,获取当前交互的要素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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 22:55:29