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

Next.js 13中Client Component引入Leaflet报window未定义求助

问题

将Leaflet导入到标记为'use client'的Foo组件所引入的文件中时,服务器仍会执行该代码并抛出ReferenceError: window is not defined错误。重试后网站能正常渲染,尝试在useEffect内使用动态导入但无效。

报错日志

ReferenceError: window is not defined
    at eval (webpack-internal:///(sc_client)/./node_modules/leaflet/dist/leaflet-src.esm.js:305:17)
    at Object.(sc_client)/./node_modules/leaflet/dist/leaflet-src.esm.js (path/to/app.next/server/app/page.js:473:1)
    at __webpack_require__ (path/to/app.next/server/webpack-runtime.js:33:42)
    at eval (webpack-internal:///(sc_client)/./src/js/index.js:5:89)
    at Object.(sc_client)/./src/js/index.js (path/to/app.next/server/app/page.js:1845:1)
    at __webpack_require__ (path/to/app.next/server/webpack-runtime.js:33:42)
    at eval (webpack-internal:///(sc_client)/./src/components/Foo.jsx:9:70)
    at Object.(sc_client)/./src/components/Foo.jsx (path/to/app.next/server/app/page.js:1834:1)

# more logs ...

相关代码

app/page.js

// app/page.js

import Foo from '@components/Foo';

export default function Base() {
  return (
    <main>
      <Foo />
    </main>
  );
}

components/Foo.jsx

// components/Foo.jsx

'use client';

import { useEffect } from 'react';
import { test } from '../js/index.js';

export default function Foo() {
  useEffect(() => {
    test();
  }, []);
  return <div>foo</div>;
}

js/index.js

// js/index.js

// Error thrown here cause calling window
import * as L from 'leaflet/dist/leaflet-src.esm.js';

const test = () => {
  console.log(L);
};

export { test };
解决方案

问题根源:尽管Foo组件标记了'use client',但它顶层导入的../js/index.js会在服务器端渲染阶段被加载执行,而Leaflet的ESM模块在导入时就会访问window对象,导致服务器报错。

以下是几种可行的解决方法:

方法一:在函数内部动态导入Leaflet

修改js/index.js,将Leaflet的导入移到test函数内部,确保仅在客户端执行时才加载Leaflet:

// js/index.js
const test = async () => {
  // 动态导入Leaflet,仅在客户端执行
  const L = await import('leaflet/dist/leaflet-src.esm.js');
  console.log(L);
};

export { test };

同时更新Foo.jsx中的useEffect以支持异步调用:

// components/Foo.jsx
'use client';

import { useEffect } from 'react';
import { test } from '../js/index.js';

export default function Foo() {
  useEffect(() => {
    const runTest = async () => {
      await test();
    };
    runTest();
  }, []);
  return <div>foo</div>;
}

方法二:在组件中动态导入整个工具模块

直接在Foo.jsx的useEffect中动态导入../js/index.js,避免顶层导入导致服务器端加载:

// components/Foo.jsx
'use client';

import { useEffect } from 'react';

export default function Foo() {
  useEffect(() => {
    const loadAndRun = async () => {
      const { test } = await import('../js/index.js');
      test();
    };
    loadAndRun();
  }, []);
  return <div>foo</div>;
}

此方法下,js/index.js中的Leaflet导入可以保留原样,但由于模块是在客户端动态加载的,window对象已存在,不会报错。

方法三:使用Next.js的动态导入禁用SSR

在app/page.js中使用Next.js的dynamic函数导入Foo组件,并禁用服务器端渲染:

// app/page.js
import dynamic from 'next/dynamic';

// 禁用SSR,Foo组件完全在客户端渲染
const Foo = dynamic(() => import('@components/Foo'), { ssr: false });

export default function Base() {
  return (
    <main>
      <Foo />
    </main>
  );
}

这种方法会让Foo组件及其所有依赖完全跳过服务器端渲染,直接在客户端加载执行,从根源避免window未定义的问题。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 20:40:29