你以为你用了 ES Module,就自动开启 Tree Shaking 了?
很遗憾,大多数情况下——并没有真正生效。
很多项目打包后:
- 明明没用的代码还在
- bundle 体积异常膨胀
- 优化了半天效果不明显
问题很可能出在一个你没注意的地方:package.json里的sideEffects
先说结论(别绕)
Tree Shaking 能不能真正生效,不只取决于 ES Module,还取决于打包器是否“敢删你的代码”。
而sideEffects,就是这个“信任开关”。
sideEffects 到底是啥?
它是写在package.json里的一个字段:
{ "sideEffects": false }它的含义是:这个项目里的文件没有副作用,可以安全删除未引用代码
什么是“副作用”?(这是关键)
副作用的定义很简单:执行这个模块时,会对外部产生影响(哪怕你没用它的导出)
有副作用的代码
// utils.jsconsole.log('hello')// 会执行// theme.js document.body.style.background = 'red'// index.js import './global.css' // 会影响页面样式这些都属于“副作用模块”
无副作用的代码
exportfunctionadd(a,b){returna+b}👉 纯函数 + 无全局修改 = 可安全删除
为什么没有 sideEffects 就“摇不掉”?
打包器(比如 Webpack)在做 Tree Shaking 时,会非常保守:
“我删了这个文件,会不会出 bug?”
如果它无法确定:宁可保留,也不删除。结果就是:
import{a}from'./utils'// utils.jsexportconsta=1exportconstb=heavyFunction()即使b没用,也可能被打进 bundle
sideEffects: false 做了什么?
{"sideEffects":false}等价于告诉打包器:“放心删,我的代码是纯的,不会有隐藏影响”。
实际效果:
- 未引用模块 → 被删除
- bundle 体积明显下降
- Tree Shaking 真正生效
但这里有个坑(很多人会踩)
你不能无脑写:
{ "sideEffects": false }因为现实项目里通常会有:
- CSS
- polyfill
- 初始化代码
正确姿势(工程推荐):
{ "sideEffects": [ "*.css" ] }含义:JS 可以被 Tree Shaking,CSS 保留(避免被误删)。
再讲一个更隐蔽的坑(很多人不知道)
即使你写了sideEffects,Tree Shaking 仍然可能失败,常见原因:
1)使用 CommonJS
constutils=require('./utils')无法做静态分析 → 直接失效
2)动态引用
import(`./${name}.js`)路径不确定 → 无法分析
3)第三方库没写 sideEffects
你写了没用,它的代码还是不会被删
总结
你遇到以下情况时,基本可以怀疑:
- bundle 体积异常大
- Tree Shaking 看起来“没效果”
- 引入 UI 库后体积暴涨
而sideEffects,就是这条链路里最容易被忽略、但影响最大的一个开关。