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

Next.js SSG中Leaflet Canvas Marker的数据获取方案抉择

解决Next.js SSG中Leaflet Canvas Marker导入GeoJSON数据的问题

针对你的场景,提供两种适配SSG模式的可行方案,根据数据更新频率选择即可:


方案一:构建时通过getStaticProps获取数据(推荐SSG静态数据场景)

这种方式在Next.js构建阶段就获取GeoJSON数据,作为静态props传入组件,完全适配SSG模式,避免客户端额外请求,加载速度更快。

步骤1:修改页面代码,添加getStaticProps

import Head from 'next/head'
import dynamic from 'next/dynamic';

// 动态导入Map组件,禁用SSR
const MapWithNoSSR = dynamic(() => import("../components/Map"), {
  ssr: false
});

const MapPage = ({ locations }) => {
  return (
    <div>
      <Head>
        <title>Map of Peng spots</title>
      </Head>
      <h1>Map of Peng spots</h1>
      <p>Map should go here!</p>
      {/* 将构建时获取的GeoJSON数据传给Map组件 */}
      <MapWithNoSSR locations={locations} />
    </div>
  )
}

// 构建阶段获取数据(运行在服务器/构建环境)
export async function getStaticProps() {
  try {
    // 推荐直接调用API路由的处理函数(避免构建时网络请求)
    // 示例:import { getLocations } from '../pages/api/getlocations';
    // const locations = await getLocations();

    // 若必须用fetch,确保构建时能访问API地址
    const res = await fetch('http://mysite:3000/api/getlocations');
    const locations = await res.json();

    return {
      props: { locations },
      // 可选:配置增量静态再生,定期更新数据
      // revalidate: 86400 // 24小时
    };
  } catch (error) {
    console.error('获取位置数据失败:', error);
    return {
      props: { locations: [] }
    };
  }
}

export default MapPage

步骤2:修改Map组件,传递数据给Canvas组件

import { MapContainer, TileLayer, Marker, Popup } from 'react-leaflet'
import 'leaflet/dist/leaflet.css'
import 'leaflet-defaulticon-compatibility/dist/leaflet-defaulticon-compatibility.css'
import 'leaflet-defaulticon-compatibility';
import LeafletCanvasMarker from './Pengs';

// 接收页面传来的locations props
const Map = ({ locations }) => {
  return (
    <MapContainer center={[50.1109, 8.6821]} zoom={14} scrollWheelZoom={false} style={{height: "100vh", width: "100%"}}>
      <TileLayer
        url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
        attribution="&copy; <a href=&quot;http://osm.org/copyright&quot;>OpenStreetMap</a> contributors"
      />
      <Marker 
        position={[50.1109, 8.682]}
        draggable={true}
        animate={true}
      >
        <Popup>
          Hey ! you found me
        </Popup>
      </Marker>
      {/* 将locations传给Canvas组件 */}
      <LeafletCanvasMarker locations={locations} />
    </MapContainer>
  )
}

export default Map

步骤3:修改Canvas图层组件,处理GeoJSON数据

import { useEffect } from "react";
import { useMap } from "react-leaflet";
import "leaflet-canvas-marker";
import L from "leaflet";

export default function LeafletCanvasMarker({ locations }) {
  const map = useMap();

  useEffect(() => {
    if (!map || !locations?.features?.length) return;

    const ciLayer = L.canvasIconLayer({}).addTo(map);

    ciLayer.addOnClickListener(function (e, data) {
      console.log(data);
    });
    ciLayer.addOnHoverListener(function (e, data) {
      console.log(data[0]?.data?._leaflet_id);
    });

    const icon = L.icon({
      iconUrl: "https://unpkg.com/leaflet@1.7.1/dist/images/marker-icon.png",
      iconSize: [20, 18],
      iconAnchor: [10, 9],
    });

    // 转换GeoJSON坐标:Leaflet用[lat,lng],GeoJSON是[lng,lat]
    const markerLayers = locations.features.map(feature => {
      const [lng, lat] = feature.geometry.coordinates;
      return L.marker([lat, lng], { icon })
        .bindPopup(`<div>${feature.properties.name || 'Unknown Spot'}</div>`); // 根据你的GeoJSON属性调整
    });

    ciLayer.addLayers(markerLayers);

    // 组件卸载时清理图层
    return () => {
      map.removeLayer(ciLayer);
    };
  }, [map, locations]);

  return null;
}

方案二:客户端通过useEffect获取数据(适配动态数据场景)

如果GeoJSON数据需要频繁更新,或构建时无法获取数据,可以在Canvas组件内部发起客户端请求,这种方式依然适配SSG(页面本身静态生成,数据在客户端加载)。

修改Canvas图层组件即可

import { useEffect, useState } from "react";
import { useMap } from "react-leaflet";
import "leaflet-canvas-marker";
import L from "leaflet";

export default function LeafletCanvasMarker() {
  const map = useMap();
  const [locations, setLocations] = useState(null);
  const [loading, setLoading] = useState(true);

  // 客户端请求API获取数据
  useEffect(() => {
    if (!map) return;

    const fetchLocations = async () => {
      try {
        const res = await fetch('/api/getlocations'); // 用相对路径即可
        const data = await res.json();
        setLocations(data);
      } catch (error) {
        console.error('加载位置数据失败:', error);
      } finally {
        setLoading(false);
      }
    };

    fetchLocations();
  }, [map]);

  // 数据加载完成后渲染Canvas标记
  useEffect(() => {
    if (!map || !locations?.features?.length || loading) return;

    const ciLayer = L.canvasIconLayer({}).addTo(map);

    ciLayer.addOnClickListener(function (e, data) {
      console.log(data);
    });
    ciLayer.addOnHoverListener(function (e, data) {
      console.log(data[0]?.data?._leaflet_id);
    });

    const icon = L.icon({
      iconUrl: "https://unpkg.com/leaflet@1.7.1/dist/images/marker-icon.png",
      iconSize: [20, 18],
      iconAnchor: [10, 9],
    });

    const markerLayers = locations.features.map(feature => {
      const [lng, lat] = feature.geometry.coordinates;
      return L.marker([lat, lng], { icon })
        .bindPopup(`<div>${feature.properties.name || 'Unknown Spot'}</div>`);
    });

    ciLayer.addLayers(markerLayers);

    return () => {
      map.removeLayer(ciLayer);
    };
  }, [map, locations, loading]);

  return null;
}

关键注意事项

  • 方案一中,getStaticProps运行在服务器/构建环境,可直接连接PostGIS数据库(无需通过API路由),效率更高。
  • 处理GeoJSON时必须转换坐标顺序:Leaflet使用[纬度, 经度],而GeoJSON标准是[经度, 纬度]。
  • 若使用增量静态再生(revalidate),Next.js会在后台自动更新页面数据,无需重新部署。

内容的提问来源于stack exchange,提问作者nizz0k

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 06:06:26