总结:① nextjs基于服务器加载的策略,如果是组件的话建议使用动态加载的方式执行,如果是函数的话可以直接写入
useEffect。
② mock对地图请求会有拦截等问题,建议直接关闭。
找刺激打算体验nextjs。arco在支持nextjs比较稳定,整个过程体验良好,不过项目中用到高德地图的时候出现了问题。跳坑解决方案如下:
① 无法加载地图,提示 window is undefined
解:官方指示react在componentDidMount之前不会得到window,在页面渲染到浏览器后,才能找到window。由于Nextjs属于服务器渲染,只能采用禁用ssr模式。
import dynamic from 'next/dynamic'
const Gaode = dynamic(() => import('@/pages/map/gaode'), {
ssr: false,
})
...
<Gaode options={mapOptions}></Gaode>
② 地图框加载出来,但是地图数据没有。console 有跨域提醒。
答:仔细对比另外成功的网页响应,发现部分网络请求携带cookie。全局搜withCredentials,果然在mock.user.ts中找到了罪魁祸首。
Mock.XHR.prototype.withCredentials = true
直接删除。
③ 网络通信无问题,但是地图还是没有显示。
答:直接删除Mock,恢复正常。
④ 开发正常,但是通过next build打包出现问题,提示window is not defined
答:感谢高手,原文如下reactjs 关闭服务器端呈现的Next.js动态导入在生产构建上不起作用 _大数据知识库 (saoniuhuo.com)也许你根本不需要next/dynamic
,只需要在useEffect
函数体中使用简单的js动态导入即可。
(useEffect
将仅在客户端上运行)
Nextjs有一个看起来非常相似的例子:https://nextjs.org/docs/advanced-features/dynamic-import.(fuse.js页面上的第一个)
import React, { useEffect, useState, useRef } from "react";
import { OpenStreetMapProvider } from 'leaflet-geosearch';
import "leaflet/dist/leaflet.css";
import 'leaflet/dist/leaflet.css'
import 'leaflet-defaulticon-compatibility/dist/leaflet-defaulticon-compatibility.css'
import "leaflet-defaulticon-compatibility";
function Map(props) {
useEffect(async () => {
const L = await import("leaflet")
const provider = new OpenStreetMapProvider();
const results = await provider.search({ query: props.adress });
if(results.length > 0 == true) {
var map = L.map('map', {
center: [results[0].y, results[0].x],
zoom: 18,
layers:
[
L.tileLayer(
'https://tiles.stadiamaps.com/tiles/alidade_smooth_dark/{z}/{x}/{y}{r}.png',
{ attribution: ''}
),
]
})
L.marker([results[0].y, results[0].x]).addTo(map)
} else {
document.getElementById("map").style.display = "none"
}
}, [])
return <div id="map" style={{ height: "30vh"}}></div>
}
export default Map;
更新
好吧,也许在useEffect钩子中移动所有需要窗口的js有点麻烦。只导入整个组件客户端更容易。对我来说,你的示例代码看起来不错--除了你在map文件中再次动态导入leaflet这一事实:
page.jsx
import dynamic from "next/dynamic";
const MapWithNoSSR = dynamic(() =>
import("../../map"), { ssr: false });
export default function faqOnly(props){
...
return <MapWithNoSSR />
}
map.jsx
import React, { useEffect, useState, useRef } from "react";
import { OpenStreetMapProvider } from 'leaflet-geosearch';
import L from 'leaflet'
import "leaflet/dist/leaflet.css";
import 'leaflet/dist/leaflet.css'
import 'leaflet-defaulticon-compatibility/dist/leaflet-defaulticon-compatibility.css'
import "leaflet-defaulticon-compatibility";
function Map(props) {
useEffect(async () => {
const provider = new OpenStreetMapProvider();
const results = await provider.search({ query: props.adress });
if(results.length > 0 == true) {
var map = L.map('map', {
center: [results[0].y, results[0].x],
zoom: 18,
layers:
[
L.tileLayer(
'https://tiles.stadiamaps.com/tiles/alidade_smooth_dark/{z}/{x}/{y}{r}.png',
{ attribution: ''}
),
]
})
L.marker([results[0].y, results[0].x]).addTo(map)
} else {
document.getElementById("map").style.display = "none"
}
}, [])
return <div id="map" style={{ height: "30vh"}}></div>
}
export default Map;
======================下面是不断尝试过程 =====================
尝试1:依旧报错。
在config中加入withTM = require('next-transpile-modules')([
'@amap/amap-jsapi-loader'
],
尝试2
xxxxxx