Tree Shaking 原理
理解 Tree Shaking 的静态分析和死代码消除机制
问题
Tree Shaking 是如何工作的?为什么它依赖 ES Module 的静态分析?
解答
什么是 Tree Shaking
Tree Shaking 是一种通过静态分析移除 JavaScript 中未使用代码的优化技术。名字来源于”摇树”——把树上的枯叶(无用代码)摇下来。
为什么依赖 ES Module
// ES Module - 静态结构,编译时确定依赖
import { add } from './math.js'
// CommonJS - 动态结构,运行时确定依赖
const add = require('./math.js').add
ES Module 的 import/export 是静态的:
- 必须在模块顶层
- 模块路径不能是变量
- 导入导出在编译时就能确定
// ❌ 这些在 ES Module 中不允许
if (condition) {
import { foo } from './foo.js' // 语法错误
}
import { bar } from getPath() // 语法错误
CommonJS 是动态的,无法在编译时分析:
// ✅ CommonJS 允许动态导入
if (condition) {
const foo = require('./foo.js')
}
const bar = require(getPath())
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
}
// index.js - 只使用了 add
import { add } from './math.js'
console.log(add(1, 2))
打包后,subtract 和 multiply 会被移除:
// 打包结果(简化)
function add(a, b) {
return a + b
}
console.log(add(1, 2))
副作用处理
有些代码虽然没被引用,但有副作用,不能删除:
// side-effect.js
export const value = 1
// 副作用:修改了全局对象
window.globalFlag = true
// 副作用:立即执行
console.log('module loaded')
通过 package.json 标记无副作用的模块:
{
"name": "my-library",
"sideEffects": false
}
或指定有副作用的文件:
{
"sideEffects": [
"*.css",
"./src/polyfill.js"
]
}
影响 Tree Shaking 的写法
// ❌ 不利于 Tree Shaking - 导出整个对象
const utils = {
add(a, b) { return a + b },
subtract(a, b) { return a - b }
}
export default utils
// ✅ 有利于 Tree Shaking - 具名导出
export function add(a, b) { return a + b }
export function subtract(a, b) { return a - b }
// ❌ 不利于 Tree Shaking - 解构 default
import utils from './utils'
utils.add(1, 2)
// ✅ 有利于 Tree Shaking - 具名导入
import { add } from './utils'
add(1, 2)
Dead Code Elimination
Tree Shaking 是 DCE(Dead Code Elimination)的一种实现。DCE 还包括:
// 1. 不可达代码
function foo() {
return 1
console.log('永远不会执行') // 会被删除
}
// 2. 无用赋值
let x = 1
x = 2 // 第一次赋值会被删除
// 3. 条件恒为假
if (false) {
console.log('永远不会执行') // 会被删除
}
关键点
- 静态分析:ES Module 的 import/export 在编译时确定,CommonJS 在运行时确定
- 具名导出:使用
export { }而非export default对象,便于分析 - 副作用标记:通过
sideEffects字段告诉打包工具哪些模块可以安全删除 - DCE:Tree Shaking 是死代码消除的一种,专门处理模块级别的未使用导出
- 工具支持:Webpack、Rollup、esbuild 等打包工具都支持 Tree Shaking
目录