5月30日 23:35

Module Federation 性能优化应该从哪些地方下手?

Module Federation 的性能优化不是只压缩 remoteEntry.js,而是控制远程模块什么时候加载、加载多少、依赖是否重复,以及失败时页面能不能优雅降级。实践里最常见的问题是:为了拆微前端把模块拆得很碎,结果请求数、共享依赖协商和首屏等待一起变多。比较稳的做法是把首屏必须展示的模块留在 host 或提前预热,把低频功能、重组件、运营位、后台管理页交给 remote。

追问

remoteEntry.js 很大时应该怎么优化?

remoteEntry.js 主要保存容器运行时和暴露模块映射,它不应该承载大量业务代码。如果它明显变大,通常是 exposes 指向了聚合入口,或者把太多公共逻辑打进了 remote 的入口链路。取舍是:暴露粒度太细会增加维护成本,暴露太粗又会让首包变重,建议按页面级或稳定业务组件暴露,不要把整个 src/index 暴露出去。还要确认生产构建开启 tree shaking,package.json 里正确声明 sideEffects,否则看似没用的模块仍可能被保留下来。

js
new ModuleFederationPlugin({ name: 'catalog', filename: 'remoteEntry.js', exposes: { './ProductCard': './src/ProductCard' }, shared: { react: { singleton: true, requiredVersion: '^18.2.0' } } })

远程模块要不要预加载?

预加载适合“很可能马上用到、但不是首屏阻塞项”的模块,比如用户登录后大概率进入的仪表盘。可以在路由 hover、首屏空闲或权限确认后加载 remoteEntry,但不要一进站就把所有 remote 都 preload,那只是把异步成本提前了。边界在于网络环境和业务路径:移动端弱网更应该谨慎,后台系统内网环境可以更激进。踩坑是只预加载 remoteEntry,却没有预热真正的 chunk,首次渲染仍会卡一下。

js
requestIdleCallback?.(() => import('catalog/ProductCard'))

shared 依赖能带来多少性能收益?

shared 的价值是避免 React、Vue、UI 库这类大依赖重复下载和重复初始化。收益取决于团队是否真的使用兼容版本,如果每个 remote 都锁不同大版本,运行时仍可能退回本地副本。取舍是 singleton 能减少体积,但会把版本升级风险集中到一个共享实例上,尤其 React、状态库和设计系统要更谨慎。性能优化时先用 bundle analyzer 看重复依赖,再决定哪些库 shared,不要把所有依赖都共享。

CDN 和缓存应该怎么配?

业务 chunk 可以用内容哈希长期缓存,remoteEntry.js 则要短缓存或配合版本化地址,因为它负责告诉 host 最新模块在哪里。一个常见坑是 remoteEntry.js 被 CDN 缓太久,remote 已发布新 chunk,host 还拿旧映射,结果线上 404。更稳的方案是 remoteEntry 短 TTL,chunk 长 TTL,并在发布后保留一段时间的旧 chunk。这样会多占一些存储,但换来灰度和回滚时的稳定性。

性能优化怎么验证是否有效?

不要只看构建产物大小,还要看首屏 LCP、远程模块首开耗时、chunk 请求瀑布和错误率。Module Federation 的问题经常出在运行时,所以 Lighthouse 只能给一部分答案,真实用户监控更关键。可以埋点记录 remoteEntry 下载、container init、module get 和组件渲染耗时。边界是埋点本身不能阻塞主链路,失败日志也要采样,否则优化工具会变成新的性能负担。

标签:Module Federation