React-leaflet服务端渲染出现window undefined错误如何解决?
问题根因
你当前的写法存在两个核心问题,直接导致服务端渲染阶段依然触发window is not defined报错:
- 顶部直接导入了
react-leaflet的相关组件,服务端渲染阶段执行导入逻辑时,react-leaflet内部依赖window对象的代码会直接执行,和你在useEffect中是否动态导入leaflet.js无关 - 未做加载状态判断,无论leaflet依赖是否加载完成,都直接渲染
MapContainer等地图组件,服务端渲染阶段会直接执行组件逻辑触发报错
修复步骤
- 移除顶部的
react-leaflet组件导入逻辑,改为仅在客户端侧动态导入react-leaflet和leaflet依赖,避免服务端执行相关代码 - 增加加载状态判断,仅在依赖加载完成后再渲染地图组件,服务端渲染阶段返回占位元素即可
- 若使用Next.js这类SSR框架,可直接使用框架提供的动态导入能力关闭组件的SSR渲染,写法更简洁
代码示例
通用SSR方案
import React, { useEffect, useState } from 'react'; import 'leaflet/dist/leaflet.css'; const MyMapComponent = () => { const [LeafletComponents, setLeafletComponents] = useState(null); useEffect(() => { const initLeaflet = async () => { // 仅在客户端环境动态导入leaflet和react-leaflet await import('leaflet/dist/leaflet.js'); const { MapContainer, TileLayer, Marker, Popup } = await import('react-leaflet'); setLeafletComponents({ MapContainer, TileLayer, Marker, Popup }); }; initLeaflet(); }, []); // 依赖加载完成前返回占位元素 if (!LeafletComponents) return <div style={{ height: '400px' }}>地图加载中...</div>; const { MapContainer, TileLayer, Marker, Popup } = LeafletComponents; return ( <MapContainer center={[51.505, -0.09]} zoom={13} scrollWheelZoom={false} style={{ height: '400px', width: '100%' }}> <TileLayer attribution='© OpenStreetMap contributors' url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" /> <Marker position={[51.505, -0.09]}> <Popup> A pretty CSS3 popup. <br /> Easily customizable. </Popup> </Marker> </MapContainer> ); }; export default MyMapComponent;
Next.js 专属简化方案
首先将地图逻辑单独抽为组件文件MyMapComponent.jsx,写普通的react-leaflet逻辑无需额外处理,在使用组件的位置按如下方式导入:
import dynamic from 'next/dynamic'; // 动态导入地图组件,关闭SSR渲染 const MyMapComponent = dynamic(() => import('./MyMapComponent'), { ssr: false, loading: () => <div style={{ height: '400px' }}>地图加载中...</div> }); // 业务代码直接调用即可 const Page = () => { return <MyMapComponent /> };
额外注意事项
如果后续出现Marker图标不显示的问题,可在leaflet加载完成后手动配置默认图标路径:
import L from 'leaflet'; delete L.Icon.Default.prototype._getIconUrl; L.Icon.Default.mergeOptions({ iconRetinaUrl: require('leaflet/dist/images/marker-icon-2x.png'), iconUrl: require('leaflet/dist/images/marker-icon.png'), shadowUrl: require('leaflet/dist/images/marker-shadow.png'), });
内容的提问来源于stack exchange,提问作者MikeL5799
相关产品推荐
相关产品推荐

