Tree Shaking 在模块联邦中需宿主与远程端协同:远程端须细粒度 ESM 暴露、禁用全量导出;宿主端须静态命名导入、共享依赖声明无副作用且版本对齐,否则无法生效。

Tree Shaking 在模块联邦(Module Federation)中不能自动作用于远程组件库,必须通过“本地声明 + 远程暴露 + 显式按需引用”三者协同才能实现真正有效的按需加载与死代码剔除。它不是开箱即用的功能,而是需要两端(宿主应用与远程应用)共同配合的工程约束。
远程端:暴露粒度要细,且必须基于 ESM
远程应用打包时,不能只暴露一个巨型入口(如 index.js),否则 Tree Shaking 无法穿透。必须按组件/工具函数级导出,并确保使用原生 ES 模块语法:
- ✅ 正确做法:在远程模块的
exposes中逐个声明可被摇动的单元"./Button": "./src/components/Button.vue",<br>"./Icon": "./src/components/Icon.vue",<br>"./utils/debounce": "./src/utils/debounce.ts"
- ✅ 所有源文件使用
export default或export const xxx,禁用module.exports或export =(TS 中避免export =) - ⚠️ 避免在远程入口文件里做全量合并导出(如
export * from './components'),这会阻断静态分析
宿主端:导入方式决定能否触发 Tree Shaking
宿主应用不能靠 import('remote/Button') 动态加载就认为完成按需——动态 import 本身不参与构建时 Tree Shaking,它只控制加载时机。真正影响打包体积的是静态 import 语句:
- ✅ 推荐:用命名导入 + 类型感知路径
import { Button } from 'remote/Button';
搭配 TypeScript 的路径映射(compilerOptions.paths)或 Vite 别名,让 IDE 和构建工具都能识别模块边界 - ❌ 禁止:
import * as Remote from 'remote';或import remote from 'remote';——整包保留在 chunk 中 - ? 提示:若远程库未提供细粒度入口,可在宿主端用
unplugin-vue-components+ 自定义 resolver 解析模板标签,再反向生成静态命名导入,间接激活 Tree Shaking
共享依赖必须声明无副作用,且版本对齐
模块联邦中,shared 字段不仅解决重复加载,还直接影响 Tree Shaking 是否生效。若共享库自身含副作用或版本混乱,会导致整个模块被保守保留:
立即学习“Java免费学习笔记(深入)”;
- ✅ 在宿主和远程两端的
package.json中统一设置"sideEffects": false;如有 CSS 或 polyfill,显式列出:"sideEffects": ["*.css", "./src/shims.ts"] - ✅
shared配置中指定具体版本范围(如react: { singleton: true, requiredVersion: "^18.2.0" }),避免因版本错位导致多个副本共存,破坏摇树逻辑 - ⚠️ 注意:某些 UI 库(如旧版 Ant Design React)内部样式通过 JS 注入,即使你没引入 CSS 文件,也可能因副作用标记而无法剔除未用组件——此时需配合
babel-plugin-import或手动剥离样式逻辑
验证是否真正生效:不止看体积,要看模块图
仅对比打包后文件大小容易误判。真正确认 Tree Shaking 起效,需检查构建产物的模块依赖关系:
- Webpack 项目:运行
npx webpack-bundle-analyzer dist/stats.json,查看远程模块是否以独立、精简的 chunk 出现,且不含未引用的导出 - Vite 项目:启用
build.rollupOptions.output.manualChunks并结合vite-plugin-inspect查看实际打入的导出项 - 关键指标:远程模块的 chunk size 应随宿主实际导入项线性增长,而非固定为几百 KB



















