摇树 Tree‑shaking:前端打包优化技术 作者:马育民 • 2026-08-11 09:26 • 阅读:10000 # 介绍 Tree‑shaking 是**死代码消除(Dead‑code Elimination)**的前端打包优化技术,比喻:摇动依赖树,抖落没用的枯叶(未使用代码),只保留真正用到的代码,减小 bundle 体积,提升页面加载速度。 概念最早由 **Rollup** 普及,Webpack、Vite、esbuild、Terser 都支持该能力。 # 原理 **依赖 ES Module(ESM)静态模块特性** 1. ESM 的 `import / export` 只能写在顶层,**编译构建阶段就可以确定依赖关系,不是运行时才解析**。 2. 打包工具构建**模块依赖图**,标记哪些导出被引用、哪些完全没被使用。 3. Webpack/Rollup 只做标记;**真正删除代码靠压缩器 Terser**;生产模式才会完整执行整套流程。 > ❗ CommonJS(`require / module.exports`)做不到 Tree‑shaking:require 是运行时动态执行,变量可以控制导入,构建阶段无法静态确定哪些代码不用,不能安全删除代码。 ### 简单示例 `math.js` 文件代码如下: ```js export const add = (a,b) => a+b export const mul = (a,b) => a*b ``` `main.js` 文件代码如下: ``` import { add } from './math.js' console.log(add(1,2)) ``` 这里 `mul` 完全没有被导入使用,Tree‑shaking 会把 `mul` 从最终产物删掉。 # 生效必备条件 1. **源码使用 ESM `import / export`**,不能被转成 CommonJS。 - Babel:`@babel/preset‑env` 务必设置 `modules: false`,禁止把 ESM 转 require;一旦转成 CommonJS,摇树直接失效。 2. **开启生产构建模式** - Webpack:`mode: production`,自动开启 Scope Hoisting(模块合并)+ Terser 压缩;开发模式不会删除死代码。 3. 正确处理**模块副作用 sideEffects**,否则工具不敢删代码。 # Vue3 中的 Tree‑shaking Vue3 + Vite 默认 Tree‑shaking 情况: - **生产构建(`vite build`):默认开启树摇** - **开发服务(`vite dev`):不做树摇。** **提示:**Vite生产底层是 Rollup,Rollup 的 `treeshake` 默认值就是 `true`,不需要你写任何额外配置。 ### 开发环境 - 使用 **esbuild** 做即时转译,**不会做删除死代码的 tree‑shaking** - 目的:速度优先,方便调试,保留全部源码 - ✅ 现象:你写的未使用代码,浏览器网络面板里依然能看到,**这不是树摇失效,环境使然** > ⚠️ **不要在dev环境判断树摇有没有生效,必须看build后的dist产物** ### 生产打包 1. Vite 调用 Rollup 进行打包,**`treeshake: true` 默认打开** 2. 压缩默认 `minify: 'esbuild'`,esbuild 会做死代码移除,支持 `/*#__PURE__*/` 3. 只要你是标准 ESM import/export,就会执行树摇。 > ✔️ 不需要手动配置 `treeshake:true`,默认就是。 ```js // vite.config 不需要写这段,本来就是默认行为 build: { rollupOptions: { treeshake: true // 可省略,默认true } } ``` ## 但是!默认开启 ≠ 一定生效 Vite只是打开了树摇开关,遇到下面情况,树摇照样失效: ### 第三方库是 CommonJS 格式 Rollup对CommonJS只能做启发式有限分析,树摇效果很差,比如原始`lodash`。 ### 聚合对象导出破坏静态分析 ```js // 无法树摇 export default { CompA, CompB } // ✅ 支持树摇 export const CompA = ... export const CompB = ... ``` ### 组件全局注册 ```js app.component('CompA', CompA) // 组件一定会打进包,无法被摇掉 ``` ### 全部导入整个库 ```js import ElementPlus from 'element-plus' // 全量引入,树摇帮不了你 ``` 原文出处:/show_1GW3qE5LmiuK.html