Tree Shaking 必须先于压缩生效,要求全 ES6 模块、production 模式、“sideEffects”声明及无顶层副作用;Terser 需配置 drop_console、pure_funcs 等深度清理;esbuild 须在摇树后运行并设环境变量;最终需用 source-map-explorer 等验证删减效果。

要让 Terser 或 esbuild 的压缩真正“压得狠”,关键不是只盯着 minify 参数,而是先确保 Tree Shaking 已把死代码清干净——压缩只是对已精简的代码做最后瘦身。否则,压缩器只是在压缩一堆本该被删掉的冗余逻辑。
Tree Shaking 必须先跑通,压缩才有意义
Tree Shaking 是静态分析过程,不依赖运行时,但有硬性前提:
- 全部模块必须用 ES6 import/export,禁用 require / module.exports;
- 构建需处于 production 模式(如 Webpack 的 mode: 'production'、Vite 的 build 命令);
- 在 package.json 中声明 "sideEffects": false(或精确列出有副作用的文件,如 ["*.css", "src/main.js"]);
- 避免在模块顶层写带实际影响的语句,比如直接调用函数、修改 window 属性、添加事件监听等——这些会被当作“有副作用”而保留。
Terser 配置要配合摇树结果做深度清理
Terser 不仅压缩,还能进一步剔除摇树后残留的“逻辑死代码”。常用配置项直击痛点:
使用约定式提交(Conventional Commits)生成标准化的 Git 提交信息。适用于以下场景: (1) 暂存更改时需要提交信息; (2) 审查 diff 时希望获得结构化摘要; (3) 编写 PR 描述或变更日志(changelog)条目; (4) 用户明确要求“为本次更改编写提交信息”。 该功能会分析已暂存/未暂存的 diff,并生成按类型(type)划分范围的提交信息。
- compress.drop_console: true —— 删除所有 console.* 调用(注意:只删调用,不删定义;前提是 console 本身没被其他逻辑引用);
- compress.drop_debugger: true —— 移除 debugger 语句;
- compress.pure_funcs: ['console.log', 'console.warn'] —— 把指定函数标记为“纯函数”,若其返回值未被使用,整行调用可被安全移除;
- compress.passes: 2 —— 多轮压缩,第二轮能基于首轮简化后的 AST 进一步优化(例如折叠常量、消除冗余条件分支);
- mangle: { reserved: ['React', '$'] } —— 混淆变量名时保留特定全局名,避免框架报错。
esbuild 压缩快,但需手动补足摇树协同点
esbuild 默认不执行 Tree Shaking(它不处理模块依赖图),所以它通常作为“最后一道压缩工序”嵌入在已做完摇树的流程中(如 Rollup 输出后再用 esbuild --minify)。若你用 Vite 或自建 pipeline:
- 确保 esbuild 运行前,模块已被 Rollup/Webpack/Vite 完成摇树;
- 启用 --tree-shaking=true(仅限 esbuild 作为打包器时,但此时它不支持 CommonJS 外部依赖的摇树);
- 用 --define:process.env.NODE_ENV=\"production\" 替换环境变量,让 if (process.env.NODE_ENV === 'development') {} 分支被完全剔除;
- 搭配 --minify-syntax --minify-identifiers --minify-whitespace 分项控制,比全开 --minify 更可控。
验证是否真生效,别信默认设置
光看打包体积下降不保险,得确认代码确实被删了:
- 用 source-map-explorer 或 Vite 的 build.report 查看各模块实际贡献大小;
- 搜索打包产物里是否存在明确未使用的函数名(比如你只用了 lodash/debounce,却搜到 throttle 的实现);
- 在开发模式下临时加一句 import { xxx } from 'xxx'; xxx();,再切 production 构建,观察该调用及对应函数是否消失。

















