Next.js微前端加载动态组件时document未定义错误求助
Next.js 14.1.0微前端Module Federation动态加载组件报document未定义问题解决
问题背景
使用Next.js 14.1.0构建包含app1和app2的微前端应用,通过ModuleFederationPlugin从app1暴露nav组件,在app2中用next/dynamic动态加载时,出现ReferenceError: document is not defined错误,伴随Unsupported Server Component type: undefined异常。
相关配置代码
app1的next.config.js
const { ModuleFederationPlugin } = require('webpack').container; const nextConfig = { distDir: 'build', webpack: (config, options) => { const { isServer } = options; config.plugins.push( new ModuleFederationPlugin({ name:"app1", filename: "static/chunks/remoteEntry.js", remoteType: "script", exposes: { "/nav": "./app/components/nav" }, }) ); if (!isServer) { config.output.publicPath = 'http://localhost:3000/build/'; } return config; } }; module.exports = nextConfig;
app2的next.config.js(原错误配置)
const { ModuleFederationPlugin } = require('webpack').container; const path = require('path'); const nextConfig = { webpack: (config, options) => { const { isServer } = options; config.plugins.push( new ModuleFederationPlugin({ name:"app2", filename: "static/chunks/remoteEntry.js", remoteType: "script", remotes: { app1: 'app1@http://localhost:3000/_next/static/chunks/remoteEntry.js' , }, }) ); if (!isServer) { config.output.publicPath = 'http://localhost:3000/.next/'; } return config; } }; module.exports = nextConfig;
app2消费组件的page.js(原错误代码)
import dynamic from 'next/dynamic' const Nav1 = dynamic(async () => { try { const module = await import('app1/nav'); console.log("module",module); return module.default; } catch (error) { console.error("Error loading component:", error); } }, { ssr: false }); export default function Home() { return ( <div> <Nav1></Nav1> </div> ) }
完整报错信息
Error loading component: ReferenceError: document is not defined while loading "./nav" from webpack/container/reference/app1 at __webpack_require__.l (C:\Users\user\Desktop\next-js-demo\app2\.next\server\webpack-runtime.js:145:28) at C:\Users\user\Desktop\next-js-demo\app2\.next\server\app\page.js:124:22 at new Promise (<anonymous>) at webpack/container/reference/app1 (C:\Users\user\Desktop\next-js-demo\app2\.next\server\app\page.js:122:18) at __webpack_require__ (C:\Users\user\Desktop\next-js-demo\app2\.next\server\webpack-runtime.js:33:42) at handleFunction (C:\Users\user\Desktop\next-js-demo\app2\.next\server\webpack-runtime.js:236:31) at C:\Users\user\Desktop\next-js-demo\app2\.next\server\webpack-runtime.js:255:15 at Array.forEach (<anonymous>) at __webpack_require__.f.remotes (C:\Users\user\Desktop\next-js-demo\app2\.next\server\webpack-runtime.js:218:36) at C:\Users\user\Desktop\next-js-demo\app2\.next\server\webpack-runtime.js:111:40 at Array.reduce (<anonymous>) at __webpack_require__.e (C:\Users\user\Desktop\next-js-demo\app2\.next\server\webpack-runtime.js:110:67) at Nav1.loadableGenerated.modules [as loader] (webpack-internal:///(rsc)/./app/page.js:12:50) at eval (webpack-internal:///(rsc)/./node_modules/next/dist/shared/lib/lazy-dynamic/loadable.js:33:58) at ev (C:\Users\user\Desktop\next-js-demo\app2\node_modules\next\dist\compiled\next-server\app-page.runtime.dev.js:35:351818) at ej (C:\Users\user\Desktop\next-js-demo\app2\node_modules\next\dist\compiled\next-server\app-page.runtime.dev.js:35:264782) at C:\Users\user\Desktop\next-js-demo\app2\node_modules\next\dist\compiled\next-server\app-page.runtime.dev.js:35:276953 at Object.toJSON (C:\Users\user\Desktop\next-js-demo\app2\node_modules\next\dist\compiled\next-server\app-page.runtime.dev.js:35:281723) at stringify (<anonymous>) at C:\Users\user\Desktop\next-js-demo\app2\node_modules\next\dist\compiled\next-server\app-page.runtime.dev.js:35:268079 at ez (C:\Users\user\Desktop\next-js-demo\app2\node_modules\next\dist\compiled\next-server\app-page.runtime.dev.js:35:268158) at eH (C:\Users\user\Desktop\next-js-demo\app2\node_modules\next\dist\compiled\next-server\app-page.runtime.dev.js:35:268559) at Timeout._onTimeout (C:\Users\user\Desktop\next-js-demo\app2\node_modules\next\dist\compiled\next-server\app-page.runtime.dev.js:35:265057) at listOnTimeout (node:internal/timers:573:17) at process.processTimers (node:internal/timers:514:7) ⨯ Internal error: Error: Unsupported Server Component type: undefined at ej (C:\Users\user\Desktop\next-js-demo\app2\node_modules\next\dist\compiled\next-server\app-page.runtime.dev.js:35:264886) at ej (C:\Users\user\Desktop\next-js-demo\app2\node_modules\next\dist\compiled\next-server\app-page.runtime.dev.js:35:264766) at ez (C:\Users\user\Desktop\next-js-demo\app2\node_modules\next\dist\compiled\next-server\app-page.runtime.dev.js:35:267802) at eH (C:\Users\user\Desktop\next-js-demo\app2\node_modules\next\dist\compiled\next-server\app-page.runtime.dev.js:35:268559) at Timeout._onTimeout (C:\Users\user\Desktop\next-js-demo\app2\node_modules\next\dist\compiled\next-server\app-page.runtime.dev.js:35:265057) at listOnTimeout (node:internal/timers:573:17) at process.processTimers (node:internal/timers:514:7)
依赖版本
{ "next": "14.1.0", "react": "^18", "react-dom": "^18", "webpack": "^5.90.1" }
问题原因
- Remote地址不匹配:app2配置中app1的remoteEntry指向
_next目录,但app1的distDir设置为build,实际文件在build/static/chunks/下,导致加载失败触发异常。 - publicPath配置错误:app2的publicPath指向了app1的端口(3000),应该指向自身的部署地址(如3001)。
- Server Component冲突:Next.js 14 App Router页面默认是Server Component,即使
dynamic设置ssr: false,加载逻辑仍可能在服务端执行,而服务端无document对象。 - 错误处理缺失:动态加载失败后返回undefined,引发
Unsupported Server Component type: undefined错误。
修复方案
1. 修正app2的next.config.js配置
const { ModuleFederationPlugin } = require('webpack').container; const nextConfig = { webpack: (config, options) => { const { isServer } = options; config.plugins.push( new ModuleFederationPlugin({ name:"app2", filename: "static/chunks/remoteEntry.js", remoteType: "script", remotes: { // 修正为app1的build目录下的remoteEntry地址 app1: 'app1@http://localhost:3000/build/static/chunks/remoteEntry.js' , }, }) ); if (!isServer) { // 修正为app2自身的端口地址(假设app2运行在3001) config.output.publicPath = 'http://localhost:3001/.next/'; } return config; } }; module.exports = nextConfig;
2. 修改app2的page.js为客户端组件并完善错误处理
'use client'; // 标记为客户端组件,确保所有逻辑在浏览器执行 import dynamic from 'next/dynamic' const Nav1 = dynamic(async () => { try { const module = await import('app1/nav'); return module.default; } catch (error) { console.error("Error loading component:", error); // 加载失败时返回备用组件,避免返回undefined return () => <div>导航组件加载失败</div>; } }, { ssr: false }); export default function Home() { return ( <div> <Nav1></Nav1> </div> ) }
3. 确保app1的nav组件兼容客户端环境
如果nav组件使用了浏览器API,添加'use client'标记并将浏览器相关逻辑放在useEffect中:
// app1/app/components/nav.js 'use client'; import { useEffect, useState } from 'react'; export default function Nav() { const [windowWidth, setWindowWidth] = useState(0); useEffect(() => { // 仅在客户端执行的逻辑 setWindowWidth(window.innerWidth); const handleResize = () => setWindowWidth(window.innerWidth); window.addEventListener('resize', handleResize); return () => window.removeEventListener('resize', handleResize); }, []); return ( <nav className="nav-container"> <h2>App1导航组件</h2> <p>当前窗口宽度:{windowWidth}px</p> </nav> ); }
4. 验证修复
- 启动app1:
npm run dev - 启动app2(确保端口与配置一致,如3001):
npm run dev - 访问app2的首页,确认导航组件正常加载
内容的提问来源于stack exchange,提问作者Renuka Tamboli
相关产品推荐
相关产品推荐

