Tree Shaking 能否删掉无用代码取决于模块是否有副作用;需通过 package.json 的 sideEffects 字段声明,设为 false 表示全无副作用,或指定含副作用的文件(如 CSS、polyfill),外层执行语句须封装进函数以避免误判。

Tree Shaking 能否真正删掉无用代码,关键不在于“有没有导入”,而在于“模块有没有副作用”。只要构建工具无法确认某模块是“纯的”,它就会保守保留整块代码——哪怕你只用了其中一行函数。
明确声明副作用状态
在 package.json 中设置 sideEffects 字段是最直接的控制方式:
- "sideEffects": false:告诉打包工具“本项目所有文件都无副作用”,可放心摇掉未使用导出
- "sideEffects": ["./src/polyfill.js", "./styles/index.css"]:仅列出真实存在副作用的文件(如注入全局样式、执行 polyfill、监听 DOM 事件等),其余文件仍可被安全清理
- 注意:sideEffects: false 不代表禁止副作用,而是承诺“这些副作用不会影响其他模块行为”,比如 CSS 文件本身有副作用(向页面插入样式),但它不干扰 JS 逻辑,所以需显式列入数组
避免顶层副作用代码
模块最外层(import/export 之外)的任何执行语句,都会被识别为副作用:
- ❌ 不要写:
console.log('init')、window.myLib = {}、document.addEventListener(...) - ✅ 正确做法:把副作用逻辑封装进函数里,只在真正调用时执行;或移到专用入口文件(如
main.js),并在sideEffects数组中声明该文件 - 例如:
export function init() { document.body.classList.add('ready'); }—— 这样即使没调用init,也不会阻碍其他函数被摇掉
区分“有副作用”和“被副作用影响”
有些代码看似“没导出也没调用”,但因副作用被保留,其实只是误判:
立即学习“Java免费学习笔记(深入)”;
- 像
import './reset.css'这类仅用于加载资源的导入,本身没有 JS 导出,但会触发样式注入——它必须出现在sideEffects列表中,否则可能被误删 - 而
import { debounce } from 'lodash-es'是纯函数导入,只要 lodash-es 本身声明了sideEffects: false,debounce 就能单独保留,其余函数自动剔除 - 检查依赖包是否支持 Tree Shaking:优先选用
lodash-es替代lodash,date-fns替代moment
验证副作用是否真被绕过
别只看配置,动手验证才可靠:
- 构建后查看产物文件,搜索一个明显未使用的导出名(如
export function unusedHelper()),确认它没出现在最终 bundle 中 - 临时添加一句
import { unusedFn } from './utils';,观察打包体积是否增大——若没变化,说明 Tree Shaking 已生效且副作用没干扰 - 用
webpack-bundle-analyzer或 Vite 的build.report查看模块引用关系,确认无用模块确实未进入依赖图


















