tree shaking是前端工程化中常用的代码优化手段,核心目标是在打包阶段剔除项目中未被使用的代码,减少最终产物的体积。它的实现和javascript的模块系统、打包工具的分析逻辑紧密相关,下面我们来详细拆解它的实现方式和运行原理。

tree shaking的前置条件
tree shaking生效的核心前提是使用ES_module模块语法,也就是使用import和export来导入导出模块。因为ES module是静态分析的,在代码编译阶段就能确定模块的依赖关系和导出内容,而CommonJS的require和module.exports是动态加载的,无法在编译阶段确定依赖,所以无法支持tree shaking。
tree shaking的工作流程
tree shaking的完整工作流程可以分为三个核心步骤,下面我们逐一说明。
1. 收集模块依赖与导出信息
打包工具在读取项目入口文件后,会从入口开始递归解析所有依赖的模块,同时记录每个模块的导出列表和导入关系。比如下面两个模块:
// math.js 导出模块
export const add = (a, b) => a + b;
export const minus = (a, b) => a - b;
export const multiply = (a, b) => a * b;
// main.js 入口模块
import { add } from './math.js';
console.log(add(1, 2));
打包工具解析后会记录math.js有三个导出:add、minus、multiply,而main.js只导入了add,minus和multiply没有被任何模块导入使用。
2. 标记无用代码
打包工具会根据收集的导入关系,给所有导出的成员打上标记。如果被导出的成员没有被其他模块导入,就会被标记为无用代码。上面的例子中,minus和multiply没有被main.js或者其他模块导入,因此会被标记为待剔除的无用代码。
3. 剔除无用代码
在生成最终打包文件时,打包工具会移除所有被标记为无用的代码。上面的例子中,最终打包产物里只会保留add函数和main.js中的调用逻辑,minus和multiply的相关代码会被完全删除。
不同打包工具中的实现差异
常见的打包工具webpack和rollup都支持tree shaking,但实现细节略有不同。
webpack中的tree shaking
webpack从2.0版本开始支持tree shaking,需要配合mode: 'production'配置使用,同时需要确保babel等转译工具不会把ES module转换成CommonJS语法。如果使用了babel,需要关闭modules转换配置:
// babel.config.js 配置
module.exports = {
presets: [
['@babel/preset-env', {
modules: false, // 不转换ES module语法,保留静态分析能力
targets: '> 0.25%, not dead'
}]
]
};
webpack的tree shaking依赖terser插件在代码压缩阶段完成无用代码的剔除,同时会结合作用域分析进一步优化代码。
rollup中的tree shaking
rollup是原生支持ES module的打包工具,tree shaking是其核心特性之一,默认就会开启。rollup的静态分析能力更强,能够更精准地识别无用代码,甚至能处理一些webpack无法识别的场景。
实际使用中的注意事项
- 确保所有模块都使用ES module语法,避免混合使用CommonJS,否则会导致tree shaking失效。
- 不要在导出模块中使用动态导出逻辑,比如根据条件动态export不同内容,这会干扰静态分析。
- 第三方库如果提供ES module版本,优先引入对应的版本,比如
lodash-es而不是lodash,才能享受tree shaking带来的体积优化。 - 避免在模块顶层执行有副作用的代码,比如直接在模块中操作DOM、修改全局变量,这类代码即使没有被导入也可能会被保留,需要在package.json中配置
"sideEffects"字段告诉打包工具哪些文件是有副作用的。
简单示例验证
我们可以通过rollup打包一个简单的示例来验证tree shaking的效果,先看两个源文件:
// utils.js
export const fn1 = () => 'fn1';
export const fn2 = () => 'fn2';
// index.js
import { fn1 } from './utils.js';
console.log(fn1());
使用rollup打包后的产物如下,可以看到fn2的代码已经被完全移除:
// 打包后的代码 const fn1 = () => 'fn1'; console.log(fn1());
通过这个示例可以直观地看到tree shaking的实际效果,未被使用的fn2函数没有出现在最终的打包结果中。
javascripttree_shakingES_modulewebpackrollup修改时间:2026-06-09 09:00:28