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

T3栈中集成React-Leaflet构建Next.js时出现构建失败问题

问题分析

你遇到的window is not defined错误,核心原因有两个:

  1. ReactMap被当作路由页面预渲染:你的ReactMap.tsx放在pages/Estate目录下,Next.js会自动将其识别为路由页面/Estate/ReactMap,构建时会尝试服务器端预渲染这个页面。而leaflet库在导入阶段就会访问window对象,导致服务器端执行时报错。
  2. 组件外部代码触发window访问:ReactMap.tsx中const markerIcon = new L.Icon(...)这行代码在组件外部执行,不管组件是否渲染,服务器端构建时都会运行这段代码,直接触发window未定义的错误。
解决方案

方案一:将ReactMap改为普通组件(推荐)

把ReactMap.tsx从pages目录移到components目录下,避免Next.js将其当作路由页面预渲染,仅作为Home页面的动态导入组件使用。同时调整组件内的代码,确保所有依赖window的逻辑只在客户端执行:

修改后的ReactMap.tsx关键部分:

import 'leaflet/dist/leaflet.css';
import { useState, useMemo } from 'react';
import {
    MapContainer,
    TileLayer,
    useMapEvents,
    Marker,
    Polyline,
    WMSTileLayer
} from 'react-leaflet';
import { getEstateInfo } from '../../server/api/norkart';
import { EstateInfo } from '../../types';

const isServer = (): boolean => typeof window === 'undefined';

const LocationMarker = ({ markerIcon }: { markerIcon: any }) => {
    const [position, setPosition] = useState<any>();
    const [estateInfo, setEstateInfo] = useState<EstateInfo>();

    useMapEvents({
        click(e) {
            setPosition(e.latlng);
            getEstateInfo([e.latlng.lat, e.latlng.lng]).then((data) => {
                setEstateInfo(data);
            });
        }
    });

    return position === undefined ? null : (
        <div>
            <Marker position={position} icon={markerIcon} />
            {estateInfo && estateInfo.Geometri && (
                <Polyline
                    pathOptions={{
                        color: 'purple',
                        opacity: 1,
                        fillColor: 'black',
                        fillOpacity: 0.5
                    }}
                    positions={estateInfo.Geometri}
                />
            )}
        </div>
    );
};

const Map = () => {
    // 将L.Icon的创建移到组件内部,仅在客户端执行
    const markerIcon = useMemo(() => {
        if (isServer()) return null;
        // 延迟导入L对象,避免服务器端加载
        const L = require('leaflet');
        require('leaflet-defaulticon-compatibility');
        require('leaflet-defaulticon-compatibility/dist/leaflet-defaulticon-compatibility.css');
        
        return new L.Icon({
            iconUrl: './marker.png',
            iconRetinaUrl: './marker.png',
            iconAnchor: [32, 64],
            iconSize: [64, 64]
        });
    }, []);

    if (isServer()) return null;

    return (
        <div>
            <MapContainer
                center={[58, 8]}
                zoom={12}
                style={{
                    height: '100vh',
                    borderRadius: '5',
                    position: 'relative'
                }}
            >
                <TileLayer
                    attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
                    url='https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png'
                />
                {false && (
                    <WMSTileLayer
                        layers='webatlas-orto-newup'
                        url='https://www.webatlas.no/maptiles/wms'
                        maxZoom={21}
                        minZoom={19}
                        attribution='&copy; 2023 Norkart AS/Geovekst og kommunene/OpenStreetMap/NASA, Meti'
                    />
                )}
                <LocationMarker markerIcon={markerIcon} />
            </MapContainer>
        </div>
    );
};

export default Map;

方案二:保留路由页面,禁用该页面的SSR

如果你需要保留/Estate/ReactMap这个路由页面,在ReactMap.tsx顶部添加以下代码,强制该页面仅在客户端渲染:

// 强制页面完全在客户端渲染,跳过服务器端预渲染
export const dynamic = "force-dynamic";

同时,把组件外部的markerIcon创建逻辑移到组件内部,确保只在客户端执行(参考方案一中的useMemo写法)。

额外注意事项
  • 移除next.config.js中的styledComponents配置项,Next.js会识别为无效配置(构建日志中已有警告),如果需要启用styled-components,应该放在compiler配置下:
    module.exports = {
      compiler: {
        styledComponents: true,
      },
    };
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 09:32:01