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

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"
}

问题原因

  1. Remote地址不匹配:app2配置中app1的remoteEntry指向_next目录,但app1的distDir设置为build,实际文件在build/static/chunks/下,导致加载失败触发异常。
  2. publicPath配置错误:app2的publicPath指向了app1的端口(3000),应该指向自身的部署地址(如3001)。
  3. Server Component冲突:Next.js 14 App Router页面默认是Server Component,即使dynamic设置ssr: false,加载逻辑仍可能在服务端执行,而服务端无document对象。
  4. 错误处理缺失:动态加载失败后返回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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 20:23:10