Module Federation 加载失败时如何调试?
Module Federation 的问题排查要先分层,不要一看到报错就改 shared。一次 remote 加载失败,可能发生在网络层、remoteEntry 执行层、共享依赖协商层、模块暴露路径层,也可能只是路由或样式副作用。排查顺序应该从“文件能不能拿到”开始,再看“容器能不能初始化”,最后才看业务代码。
先确认 remoteEntry 是否真的可用
打开浏览器 Network,检查 remoteEntry.js 是否 200、content-type 是否正常、是否被 CDN 缓存到旧版本、是否有 CORS 或 CSP 拦截。很多线上问题不是 Module Federation 本身坏了,而是发布路径、publicPath 或缓存策略错了。
js// remote devServer 必须允许 shell 跨域访问 devServer: { port: 3001, headers: { 'Access-Control-Allow-Origin': '*' } }
如果 remoteEntry 返回的是 HTML,通常是路径被网关重写到了首页;如果状态码是 200 但执行报语法错误,要检查构建目标和浏览器兼容。这个阶段不要先动 shared,否则会把简单网络问题排复杂。
再看容器初始化和暴露路径
Shell 里写的 import('user/App'),必须和 remote 的 exposes 完全对应。大小写、斜杠、别名错一个都会失败。可以在控制台检查 window.user 是否存在,再手动调用 get 方法定位问题。
jsawait __webpack_init_sharing__('default') const container = window.user await container.init(__webpack_share_scopes__.default) const factory = await container.get('./App') const Module = factory() console.log(Module)
如果 window.user 不存在,问题在 remoteEntry 加载或 remote name;如果 get('./App') 失败,问题多半在 exposes;如果 factory 执行后业务报错,再进入组件内部调试。
shared 依赖怎么排查?
共享依赖问题通常表现为 hooks invalid、context 失效、样式库重复注入、运行时版本不匹配。先确认 React 是否只有一份,再看 requiredVersion 和 singleton 配置。不要把所有依赖都 eager,eager 会提高首屏包体,也可能让加载顺序更难控。
jsshared: { react: { singleton: true, requiredVersion: '^18.2.0' }, 'react-dom': { singleton: true, requiredVersion: '^18.2.0' } }
取舍在于:共享能减少重复加载,但会增加团队之间的版本耦合。不稳定或频繁升级的业务库,未必适合共享。
Source Map 和日志应该怎么配?
调试联邦应用时,Source Map 要能区分 Shell 和 remote,否则堆栈里只看到一堆打包后的 chunk 名称。建议 remote 构建时设置独立 namespace,并在错误上报里带上 remoteName、remoteVersion、exposedModule 和 manifestVersion。这样线上看到 Loading chunk failed 时,才能判断是 CDN 缓存、发布缺文件,还是某个用户命中了旧清单。Source Map 不一定要公开暴露给所有用户,生产环境可以上传到监控平台,用错误 ID 反查源码位置。
有哪些工具值得放进排查流程?
浏览器 DevTools 是第一工具,Network 看入口和 chunk,Console 看容器初始化,Performance 看加载瀑布。React DevTools 适合确认组件树和 Context 是否跨 remote 正常传递,构建分析工具则用来看 shared 是否真的被共享。团队还可以做一个简单的 federation debug 面板,把当前 manifest、remote 版本、加载耗时、失败原因直接展示出来。边界是工具只能缩短定位时间,不能弥补发布规范缺失;如果 remoteEntry 命名不可回滚,再好的面板也只能告诉你它坏了。
还有一个容易忽略的点是环境差异。开发、测试、预发、生产最好使用同一套 manifest 结构,只替换域名和版本,不要每个环境写一份完全不同的 remote 配置。否则本地修好的问题,上线后可能因为清单字段或 CDN 路径不同再次出现。
追问
remoteEntry 明明 200,为什么还是加载失败?
先看返回内容是不是 JavaScript,而不是网关兜底返回的 index.html。再看 remoteEntry 里引用的 chunk 是否能继续加载,很多问题发生在二级 chunk 的 publicPath 上。还要检查 CSP、跨域头和 CDN 缓存,尤其是 Shell 更新了 manifest,但用户还拿着旧 remoteEntry。边界是 200 只能说明入口文件到了,不代表容器初始化成功。
怎么判断是 shared 冲突还是业务代码报错?
如果报错出现在 container.init 或共享作用域协商阶段,优先查 shared。若 container.get('./App') 能拿到 factory,执行组件时才报业务异常,就应该回到 React DevTools、Source Map 和业务日志。shared 冲突常见特征是 React hooks、context、styled-components 或路由上下文异常。踩坑是把业务异常误判为依赖冲突,结果越改 shared 越乱。
本地调试多个 remote 有什么坑?
端口、跨域、热更新和版本不一致是最常见的四类坑。Shell 本地连 remote 本地时,要确认 remote dev server 已启动且 remoteEntry 地址没有写死到测试环境。HMR 在联邦场景下不一定每次都可靠,遇到奇怪状态先刷新页面和清缓存。取舍是本地全链路调试更接近真实环境,但启动成本和不稳定因素也更多。
线上应该监控哪些指标?
至少监控 remoteEntry 加载耗时、chunk 加载失败率、container 初始化错误、模块 get 失败和降级 UI 命中次数。只看业务接口错误不够,因为联邦问题可能在页面渲染前就失败了。建议日志里带上 shell 版本、remote 名称、remote 版本和用户命中的 manifest。边界是监控不能替代降级,告警告诉你出事了,fallback 才能让用户不白屏。