Tree Shaking 依赖 ES 模块静态语法与构建工具的静态分析能力,通过扫描 import/export 构建依赖图谱标记未使用导出,但 CommonJS、副作用、动态导入等会导致失效。

Tree Shaking 的底层原理,说白了就是“不运行代码,也能知道哪些函数根本没人用”。它靠的是两样东西:ES 模块的静态语法 + 构建工具的“画图能力”。
为什么必须用 import/export?
因为 import 和 export 是写死在代码最上面的,不能藏在 if 里、也不能拼字符串路径。比如:
-
import { debounce } from 'lodash'→ 工具一眼看出你只用了 debounce -
const utils = require('lodash')→ require 是运行时才执行的,工具没法提前知道你要用哪个方法 -
import(`./pages/${page}.js`)→ 路径是变量,编译时根本不确定加载谁,Tree Shaking 就失效了
依赖图谱是怎么画出来的?
构建工具(比如 Webpack 或 Vite)会从入口文件开始,一层层“顺藤摸瓜”:
- 读到
import { foo } from './a.js',就去打开 a.js - 在 a.js 里看到
export function foo() {}和export function bar() {} - 再检查整个项目里有没有地方
import { bar }—— 如果没有,bar 就被标记为“没用” - 这个过程不执行函数,只是扫描语法,所以叫“静态分析”
为什么有时候摇不掉代码?
常见卡点不是原理不行,而是实际使用中“踩了坑”:
- 第三方库用了 CommonJS(比如老版本 lodash),导出是
module.exports = {...},工具看不懂 - 模块里有副作用(比如直接改全局变量、发请求、操作 DOM),工具不敢删,除非你在
package.json里声明"sideEffects": false - 虽然没显式 import,但代码里写了
require('./xxx')或通过字符串 eval 加载,静态分析就断链了
一句话总结
Tree Shaking 就像图书管理员整理书架:先按目录(import/export)理清每本书(模块)里有什么章节(导出),再对照借阅记录(所有 import 语句)划掉没人翻过的章节——全程不用翻开书看内容,也不用等读者来借。


















