跳到主要内容

Tree Shaking 与 sideEffects

如果你希望你的代码或开源库能够被正确地 Tree Shaking(让打包器在最终产物中自动移除未被使用的导出),需要显式告诉构建工具:哪些文件“不会在被引入时产生副作用”。最常见的做法是在 package.json 中配置 sideEffects

  • 绝大多数纯 JS/TS 工具库:可以设置为 "sideEffects": false。这表示你的模块在被 import 时不会执行影响外部环境的行为(例如修改全局变量、注册 polyfill、自动打补丁、向 window 挂载属性、自动注入样式等)。这样 Webpack/Rollup 等工具就更敢于删除“只引入了但未使用”的文件,从而获得更好的 Tree Shaking 效果。
  • 确实有副作用的文件:不要一刀切地设为 false,否则可能导致样式或初始化逻辑被错误裁剪。你可以把 sideEffects 配置成数组白名单,例如:

简单来说:sideEffects 的目的不是“优化一定更快”,而是让打包器在保证行为正确的前提下,尽可能安全地移除无用代码。


什么是 Tree Shaking?

Tree Shaking 是打包工具(Webpack/Rollup/esbuild)在生产构建时自动删除未使用的导出代码的优化手段。它依赖 ES Module 的静态结构分析。

sideEffects 配置详解

package.json 配置

{
"name": "my-library",
"sideEffects": false
}

// 或者指定有副作用的文件
{
"sideEffects": [
"*.css",
"*.scss",
"./src/polyfill.ts",
"./src/register-global.ts"
]
}

什么算”副作用”?

  • 修改全局变量(window.xxx = …)
  • 注册 polyfill(import “core-js/stable”)
  • CSS/SCSS 文件的引入(import “./style.css”)
  • 立即执行的函数表达式(IIFE)且影响外部状态
  • 模块顶层的 console.log / fetch 等调用

什么不算”副作用”?

  • 纯函数定义和导出
  • 类型定义和接口声明
  • 常量定义(export const FOO = 1)
  • 纯计算逻辑(不依赖/修改外部状态)

Webpack 中的 Tree Shaking

// webpack.config.js
module.exports = {
mode: 'production', // 生产模式自动启用
optimization: {
usedExports: true, // 标记未使用的导出
minimize: true, // 压缩时删除标记的代码
sideEffects: true, // 读取 package.json 的 sideEffects
},
};

常见问题与排查

问题 1:CSS 被错误删除

// ❌ 设置了 sideEffects: false,CSS import 被当作无副作用删除
import './button.css';

// ✅ 解决:将 CSS 加入白名单
// package.json: "sideEffects": ["*.css"]

问题 2:类的静态属性导致无法 Tree Shake

// ❌ 类的静态属性赋值被视为副作用
class MyComponent {
static displayName = 'MyComponent'; // 这行是副作用!
}

// ✅ 添加 /*#__PURE__*/ 注释
const MyComponent = /*#__PURE__*/ (() => {
class MyComponent {}
MyComponent.displayName = 'MyComponent';
return MyComponent;
})();

问题 3:barrel file(index.ts 重导出)影响 Tree Shaking

// ❌ src/index.ts(barrel file)
export { Button } from './Button';
export { Modal } from './Modal';
export { Table } from './Table';
// 即使只用了 Button,Modal 和 Table 的初始化代码也可能被保留

// ✅ 直接从具体模块导入
import { Button } from 'my-lib/Button';

验证 Tree Shaking 效果

  • 使用 webpack-bundle-analyzer 查看打包结果
  • 检查构建产物中是否包含未使用的模块
  • 使用 sideEffects-analysis 工具分析项目副作用
# 安装分析工具
pnpm add -D webpack-bundle-analyzer

# 在 webpack 配置中添加
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
plugins: [new BundleAnalyzerPlugin()]