什么是 Tree Shaking?如何确保它生效?
Tree Shaking
摇树优化:打包时移除未使用的代码(死代码),减小包体积。
// math.js
export function add(a, b) { return a + b }
export function subtract(a, b) { return a - b }
export function multiply(a, b) { return a * b }
// app.js
import { add } from './math.js'
console.log(add(1, 2))
// 打包后:subtract 和 multiply 被移除工作原理
- 依赖 ESM 的静态结构(import/export 在编译时确定)
- 打包工具分析模块依赖关系
- 标记未被导入的 export 为"未使用"
- 压缩阶段移除未使用的代码
为什么 CommonJS 不能 Tree Shake
// CommonJS — 动态加载,无法静态分析
const math = require('./math')
const fn = process.env.TEST ? math.add : math.subtract // 运行时决定
// ESM — 静态结构,编译时确定
import { add } from './math' // 明确只用了 add确保 Tree Shaking 生效
1. 使用 ESM
// ✅ ES Module
import { debounce } from 'lodash-es'
// ❌ CommonJS(不触发 Tree Shaking)
import { debounce } from 'lodash'2. package.json 配置 sideEffects
{
"name": "my-lib",
"sideEffects": false // 所有文件都没有副作用,可安全 Tree Shake
}或指定有副作用的文件:
{
"sideEffects": ["*.css", "*.scss", "./src/polyfills.js"]
}什么是副作用?
// 有副作用:修改了全局状态
window.myGlobal = 'hello'
Array.prototype.myMethod = function() {}
// 无副作用:纯导入导出
export function add(a, b) { return a + b }3. 生产模式
// Webpack
mode: 'production' // 自动开启 Tree Shaking
// Vite/Rollup
// 生产构建自动开启4. 避免整包导入
// ❌ 导入整个 lodash
import _ from 'lodash'
_.debounce(fn, 300)
// ✅ 按需导入
import debounce from 'lodash/debounce'
// 或
import { debounce } from 'lodash-es' // 配合 Tree Shaking验证 Tree Shaking
# Webpack
npx webpack --analyze
# Vite
vite build --mode production
# 查看包大小变化常见问题
1. 函数有副作用导致无法摇除
// ❌ 有副作用(访问了全局变量)
export function init() {
console.log('init') // 副作用
}
// ✅ 纯函数
export function add(a, b) {
return a + b
}2. 类的方法不会被摇除
export class MyClass {
unusedMethod() {} // 不会被摇除(可能有副作用)
}3. 动态导入无法静态分析
const lib = await import(dynamicPath) // ❌ 无法 Tree ShakeWebpack vs Vite Tree Shaking
两者都支持 Tree Shaking,Vite(Rollup)在 Tree Shaking 上通常更激进。