tree shaking 能生效的前提是 esm 的静态特性:import/export 必须在顶层、路径为字符串字面量,使构建工具可通过 ast 静态分析生成精确依赖图谱,标记并剔除未被引用的导出;commonjs 因 require 动态性无法支持,babel 需配置 modules: false 且需声明 sideeffects 才能确保优化效果。

ES Module(ESM)的静态特性是 Tree Shaking 能够生效的前提,它让构建工具在不运行代码的情况下,就能准确知道“谁用了谁”,从而安全剔除未使用的导出。
ESM 的导入导出必须写在顶层,且路径固定
ESM 要求 import/export 必须出现在模块最外层作用域,不能放在 if、函数或 try/catch 里;同时模块路径必须是字符串字面量,不能是变量拼接或动态计算。这样 Webpack、Rollup 等工具在解析时,能直接从源码中提取所有依赖声明,生成精确的依赖图谱。
- ✅ 正确(可被静态分析):
import { debounce } from './utils.js' - ❌ 错误(无法分析):
const name = 'utils'; import(`./${name}.js`)或if (cond) import('./feature.js')
静态分析不是猜,而是构建 AST 后逐节点追踪
构建工具会把每个 ESM 文件解析成抽象语法树(AST),识别所有 export 声明,并记录哪些被其他模块的 import 显式引用。从入口文件开始,沿着 import 链递归标记——被引用的导出打上“活跃”标记,其余默认为“可移除”。
Java JDK 25 来自 OpenJDK 官方归档,版本为 JDK 25,本条下载地址已指向官方 Windows x64 zip 安装包直链,适合调试旧项目或兼容旧版 Java 运行环境。
- 比如
utils.js导出了add和multiply,但只有add被main.js引入,那么multiply就会被标记为死代码 - 这个过程发生在打包早期阶段,和代码是否真的执行无关
CommonJS 为什么不行?因为它太“活”了
require() 是运行时行为:路径可以是变量、条件判断、甚至网络请求结果。构建工具无法在编译时确定它到底加载了什么,更无法判断某个 module.exports 是否被用到。所以即使你只用了 Lodash 的 debounce,整个 Lodash 包仍可能被打包进去——除非它本身是 ESM 格式且正确导出。
- Babel 若配置
modules: false,才能保留import/export不被转成require,否则 Tree Shaking 会失效 - 混用
import和require在同一文件中,也会破坏静态分析链条
副作用声明影响最终剔除结果
有些模块虽然没被显式引入,但执行时会修改全局状态(如 polyfill、CSS 注入、原型扩展)。这类代码不能随便删。通过 package.json 中的 "sideEffects": false 告诉构建工具:“本包所有文件都无副作用,没被引用就放心删”。若某些文件有副作用,需明确列出,例如:"sideEffects": ["*.css", "index.js"]。
- 没声明
sideEffects,工具会保守处理,宁可多打包也不冒险删 - 声明为
false后,哪怕某个工具函数只导出没被引用,也会被彻底移除
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










