reactjs 关闭服务器端呈现的Next.js动态导入在生产构建上不起作用

nfg76nw0  于 2022-12-29  发布在  React
关注(0)|答案(2)|浏览(156)

我正在用leaflet包建一个站点,但是这个包需要window对象,所以我把leaflet做的组件作为动态组件导入,ssr关闭,如下所示:

import dynamic from "next/dynamic";
   const MapWithNoSSR = dynamic(() => import("../../map"), {
             ssr: false
 });
 
export default function faqOnly(props){
 ...
 return (<> <MapWithNoSSR /></>)
 }

Map组件如下所示:

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";
import dynamic from "next/dynamic";

const L = dynamic(() => import("leaflet"), {
      ssr: false,
      suspense: true,
      loading: () => <p>...</p>
    });

function Map(props) {

  useEffect(async () => {
    if(window === undefined) return
    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;

当我运行npm run build时,我得到了这个错误:

ReferenceError: window is not defined
    at E:\Github\Planer\rl-planer\node_modules\leaflet\dist\leaflet-src.js:230:19
    at E:\Github\Planer\rl-planer\node_modules\leaflet\dist\leaflet-src.js:7:66
    at Object.<anonymous> (E:\Github\Planer\rl-planer\node_modules\leaflet\dist\leaflet-src.js:10:3)
    at Module._compile (node:internal/modules/cjs/loader:1101:14)
    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1153:10)
    at Module.load (node:internal/modules/cjs/loader:981:32)
    at Function.Module._load (node:internal/modules/cjs/loader:822:12)
    at Module.require (node:internal/modules/cjs/loader:1005:19)
    at require (node:internal/modules/cjs/helpers:102:18)
    at Object.<anonymous> (E:\Github\Planer\rl-planer\node_modules\leaflet-geosearch\dist\geosearch.js:1:7) {
  type: 'ReferenceError'
}

这在开发中工作得很好,但是当我尝试构建项目(下一个构建)时,它在传单包中抛出了一个“窗口未定义”错误,这是我在开发模式下工作时从未出现过的错误。
我看了这里的其他问题,但是似乎把动态导入移到组件之外可以解决除了我之外的所有人的问题。是我太笨了,还是这里有什么问题?

v09wglhw

v09wglhw1#

也许你根本不需要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;
mwkjh3gx

mwkjh3gx2#

我所做的就是创建一个Map组件,它包含了leaflet中使用window的每个模块,然后使用动态导入的nextjs导入该组件。在我的例子中,我没有对leaflet模块使用动态导入,而只对Map组件使用了动态导入。

相关问题