tree shaking 关键在于构建工具通过 es 模块静态分析识别未使用导出,并由压缩器(如 terser)在生产模式下真正擦除;它依赖 static import/export、无副作用声明、生产环境配置,不适用于 commonjs 或动态导入。

资源打包通过 Tree Shaking 移除死代码,关键不在“打包动作”本身,而在于构建工具能否静态识别哪些导出没被使用,并在压缩阶段真正擦除。它不是运行时行为,也不依赖执行逻辑,而是靠编译期的结构分析和后续优化协同完成。
必须用 ES 模块语法
Tree Shaking 只对 static import/export 生效。因为这类语句位置固定、路径明确、绑定名可提前解析,工具能直接构建依赖图。
- ✅ 正确写法:import { debounce } from 'lodash-es'; export const utils = {};
- ❌ 无效写法:const _ = require('lodash'); module.exports = {};(CommonJS 是动态的,整个模块变黑盒)
- ⚠️ 注意:import('./dynamic.js') 是动态导入,不参与静态依赖分析,其内部模块仍可能被 shake——但前提是它自己用的是 ESM 且导出未被其他静态 import 引用
告诉工具哪些代码“没副作用”
即使某个模块没被 import 引用,如果它顶层有执行逻辑(比如改 document.title、调 API、定义全局变量),Webpack 默认认为它“有副作用”,不会删掉整块代码。
- 在 package.json 中声明:"sideEffects": false(表示项目内所有文件都无副作用)
- 或更安全地列出有副作用的文件:"sideEffects": ["./src/index.css", "*.scss"]
- 对单个函数调用加注释:/*#__PURE__*/doSomething(),提示压缩器该调用可安全移除
启用生产模式并依赖压缩器清理
标记未使用代码是第一步,真正删除靠压缩阶段。Webpack 本身不直接删代码,而是交由 Terser 等工具在生成最终 bundle 时执行擦除。
- Webpack 需设 mode: 'production'(自动启用 TerserPlugin 和 usedExports)
- Rollup/Vite 默认开启,无需额外配置
- 开发模式下即使写了 usedExports: true,也只做标记,不会删代码
CSS 文件不参与 Tree Shaking
CSS 没有 export 语义,import './button.css' 被视为副作用导入——只要写了这行,整个文件内容就保留,无论其中类名是否出现在 JSX/HTML 中。
- 所谓“CSS Tree Shaking”实为类名扫描:Tailwind 靠 content 配置扫描源码字符串;UnoCSS 按需生成原子类
- 动态类名(如 class={`btn ${size}`})需显式加入 safelist 或用正则保护
- PurgeCSS 适合传统 CSS 项目,但需自定义 extractor 处理 BEM 等命名规则










