Tree Shaking需代码、配置、依赖三方协同,验证须分四步:建最小ESM环境、逐层检查unused注释/Terser删除/副作用声明、测lodash-es等库兼容性、排查常见失效原因。

Tree Shaking 不是开个配置就自动生效的魔法,它是一套需要代码写法、构建配置、依赖库三者协同的实战体系。做一次综合演练,关键不是“跑通”,而是验证每个环节是否真正起作用,并定位失效原因。
一、搭建可验证的最小实验环境
先建一个干净项目,不依赖框架,只用原生 ES 模块:
mkdir tree-shaking-demo && cd tree-shaking-demo npm init -y npm install --save-dev webpack webpack-cli terser-webpack-plugin
创建基础文件:
-
src/math.js(含两个导出,只用其一):export const add = (a, b) => a + b; export const multiply = (a, b) => a * b; export default () => console.log('default export'); -
src/index.js(仅导入add):import { add } from './math.js'; console.log(add(2, 3)); -
webpack.config.js(显式启用关键开关):const TerserPlugin = require('terser-webpack-plugin'); module.exports = { mode: 'production', entry: './src/index.js', output: { filename: 'bundle.js', path: __dirname + '/dist' }, optimization: { usedExports: true, // 标记哪些导出被使用 minimize: true, minimizer: [ new TerserPlugin({ terserOptions: { compress: { drop_console: true } } }) ] }, module: { rules: [{ test: /\.js$/, exclude: /node_modules/, use: { loader: 'babel-loader', options: { presets: [ ['@babel/preset-env', { modules: false }] // ✅ 关键:不转成 CommonJS ] } } }] } };
⚠️ 注意:
modules: false是 Babel 配置的生死线。若缺此行,Babel 会把import/export编译成require/module.exports,Tree Shaking 直接失效。
二、分步验证是否真在“摇”
不要只看最终 bundle 大小——它受压缩、混淆等干扰。要分层观察:
第1层:检查 Webpack 是否标记了未使用导出
在optimization.usedExports: true下,Webpack 会在未使用导出前插入注释,如:/* unused export multiply */
→ 构建后打开dist/bundle.js搜索unused export,确认存在。第2层:确认 Terser 是否真正删掉
TerserPlugin的compress.unused: true(默认开启)会移除带unused export注释的代码。
→ 搜索multiply字符串,应完全消失;而add的函数体仍可见(可能被内联或重命名,但逻辑保留)。-
第3层:排除副作用干扰
若math.js中有副作用(如console.log('init')或全局赋值),Webpack 默认不敢删任何导出。
→ 此时需在项目根目录package.json中声明:"sideEffects": ["*.css", "*.scss"]
或更严格地写
"sideEffects": false(表示所有模块都无副作用),否则multiply可能因“怕误删副作用”而被保留。
三、引入真实第三方库验证兼容性
很多体积问题来自 lodash 这类库。改用支持 Tree Shaking 的版本:
npm install lodash-es # ✅ 替代 lodash
-
src/utils.js:import { debounce, throttle } from 'lodash-es'; // ✅ ES 模块版 export { debounce, throttle }; -
src/index.js改为:import { debounce } from './utils.js'; debounce(() => {}, 300);
→ 构建后检查 lodash-es 中 throttle 是否被剔除(搜索 throttle 应无结果)。若没被删,大概率是 lodash-es 内部某些模块有隐式副作用,或你的 sideEffects 配置太宽松。
四、常见失效场景与修复清单
- [ ] 使用了
require()或import()动态导入 → 改为静态import { x } from 'y' - [ ] 第三方库是 CommonJS 发布(如旧版
moment)→ 换成 ESM 版(如date-fns)或找官方esm分支 - [ ] 导出用了
export default+ 命名导出混用 → 尽量统一用具名导出,避免默认导出被整体保留 - [ ]
package.json缺少"type": "module"或"sideEffects"字段 → 补上并设为false或精确路径 - [ ] Babel 配置中
modules: 'auto'或未设false→ 强制设为false
做完这四步,你得到的不是一个“能跑”的 demo,而是一个可复用的诊断模板:下次遇到体积异常,直接套用这套验证流程,就能快速定位是代码、配置,还是依赖库的问题。











