Tree Shaking 是依赖 ES Module 静态分析的代码消除机制,需代码(具名导入/导出)、配置(production 模式、sideEffects 声明)与依赖(ESM 版本库)三者协同才生效。

Tree Shaking 不是玄学,也不是一键开关就能见效的优化技巧。它是一套依赖代码结构、构建配置和工具链协同工作的静态优化机制。想真正“看清全景”,得从三个层面入手:它为什么能工作、它在什么条件下失效、以及你怎么确认它真的起了作用。
Tree Shaking 为什么只认 ES 模块
核心就一句话:只有 import/export 是编译时确定的。打包工具(Webpack/Rollup/Vite)在不运行代码的前提下,靠词法分析就能画出完整的依赖图——谁导入了谁、谁导出了什么、哪些导出名被实际使用了。而 require 是运行时才决定加载什么,工具根本没法提前判断,自然无法标记“没用的代码”。所以,哪怕你写的是 import { throttle } from 'lodash',如果底层 lodash 发布的是 CommonJS 版本(比如 npm 上默认的 lodash),Babel 又把它转成了 require,那 Tree Shaking 就彻底失效。
哪些写法会悄悄破坏 Tree Shaking 效果
- 用
import *或import default引入整包:比如import _ from 'lodash'或import * as utils from './utils',工具无法推断你到底用了其中哪几个函数,大概率全保留 - 导出方式不合理:写成
export default { a() {}, b() {} },使用者哪怕只调utils.a,整个对象字面量仍可能被打包进来;换成具名导出export function a() {}才安全 - 模块有副作用但没声明:比如某个文件只是执行
Element.prototype.$ = ...或监听全局事件,没导出任何东西,也没被显式引入,却会被误删。这时需要在package.json中配"sideEffects": false或精确列出有副作用的文件路径
怎么验证 Tree Shaking 到底有没有生效
别只看文档说“Webpack 5 默认开启”,要动手验证:
使用约定式提交(Conventional Commits)生成标准化的 Git 提交信息。适用于以下场景: (1) 暂存更改时需要提交信息; (2) 审查 diff 时希望获得结构化摘要; (3) 编写 PR 描述或变更日志(changelog)条目; (4) 用户明确要求“为本次更改编写提交信息”。 该功能会分析已暂存/未暂存的 diff,并生成按类型(type)划分范围的提交信息。
- 构建后打开产物文件,搜索你明确没用的函数名,看是否还存在
- 用
webpack-bundle-analyzer可视化分析,点开模块看内部哪些导出被标记为 “unused” - 做体积对比实验:在入口加一行
import { unusedHelper } from './helpers',再构建一次,观察 JS 文件大小是否增加——如果不增,说明 Tree Shaking 正常工作
对开发者最实在的建议
写代码时养成两个习惯就够了:
- 第三方库优先选提供 ESM 版本的,比如用
lodash-es替代lodash,用date-fns替代moment - 自己写工具模块时,坚持用具名导出 + 按需引入,避免 default 导出大对象或 index.js 全量 re-export










