Skip to content

参考

基于 Webpack / Rollup / Vite / Tailwind CSS 官方文档编写,对照 Webpack 5.108、Rollup 4、Vite 8(Rolldown)、Tailwind v4 行为

速查

  • 本质:构建期死代码消除;ESM 静态分析 + 标记 + 压缩期删除三步
  • sideEffects 三态false / Array<string> 白名单 / 缺省(保守保留)
  • Webpack 五开关usedExports / sideEffects / innerGraph / concatenateModules / minimizemode=production 全开
  • Rollup treeshakesmallest / safest / recommended(默认 true=recommended)
  • 注解/*#__PURE__*/(单次调用、通用)/ /*@__NO_SIDE_EFFECTS__*/(整函数声明、Rollup 专属)
  • Vite 8:Rolldown 替代 Rollup;build.rolldownOptions 替代 build.rollupOptionsoutput.codeSplitting 替代 output.manualChunks
  • Tailwind v4:默认 tree-shaking + @source / v3 content 数组 / PurgeCSS 独立工具
  • 核心原则:ESM 前提 + 库声明 sideEffects + 生产模式 + minimize 开 + 注解精确
  • 完整说明见 入门 / 核心机制与配置

sideEffects 三态语义

取值含义典型用法
false整包可 shake纯 ESM 工具库 / 组件库的 JS 部分
Array<string>白名单 glob含 CSS / polyfill / 全局补丁的包
缺省保守保留整模块不推荐——等于放弃优化

典型白名单

json
{
  "sideEffects": [
    "*.css",
    "*.scss",
    "./src/polyfills.js",
    "./src/global-patch.js"
  ]
}

Webpack optimization 完整开关

选项默认(production)默认(development)作用
usedExportstruetrue(Webpack 5+)标记每个模块的导出哪些被使用
sideEffectstruetrue(Webpack 5+)package.json sideEffects 跳过整模块
innerGraphtruetrue(Webpack 5+)未使用导出的内部依赖图分析
providedExportstruetrue(Webpack 5+)收集模块提供了哪些导出
mangleExports'deterministic'false短名压缩导出标识符
concatenateModulestruefalseScope Hoisting:合并模块作用域
minimizetruefalse真正执行删除(Terser / esbuild)
minimizer[new TerserPlugin()][]压缩器插件列表

关键关系

  • mode: 'production' 一键启用上述全部
  • usedExports 标记 + minimize: true 删除 = 死代码真正消失
  • 只标 usedExports 不开 minimize:死代码仍在 bundle
  • sideEffectsusedExports 更彻底(跳过整模块 vs 标记单导出)

Webpack Rule 级覆盖

ts
module: {
  rules: [
    {
      test: /\.js$/,
      sideEffects: false,        // 按模块规则覆盖 sideEffects
    },
  ],
}

Rollup treeshake 完整子选项

选项默认含义
annotationstrue尊重 /*#__PURE__*/ / /*@__NO_SIDE_EFFECTS__*/ 注解
moduleSideEffectstrue模块副作用假设;可设 false / 'no-external' / string[] / (id, external) => boolean
manualPureFunctions[]始终视为无副作用的函数名数组,如 ['clsx', 'css']
propertyReadSideEffectstrue属性读 obj.x 可能副作用(getter)
tryCatchDeoptimizationtruetry/catch 内代码不被 shake(polyfill 检测易卡这里)
unknownGlobalSideEffectstrue未知全局变量访问视为副作用
correctVarValueBeforeDeclarationfalse严格 var 初始化前值

三预设差异

预设关键差异
'smallest'最激进:moduleSideEffects: falsetryCatchDeoptimization: false,体积最小但有正确性风险
'safest'最保守:保留更多代码
'recommended'(默认)平衡:annotations: truepropertyReadSideEffects: truetryCatchDeoptimization: true

Vite 8 Rolldown 迁移对照

Vite 7(Rollup)Vite 8(Rolldown)状态
build.rollupOptionsbuild.rolldownOptions入口改名
output.manualChunks(对象)已移除静默不生效
output.manualChunks(函数)output.codeSplitting(弃用警告)函数形式已弃用
Rollup treeshake 全部子选项Rolldown 对应选项选项名兼容,行为对齐

编译期常量

表达式prod build 替换
import.meta.env.DEVfalse
import.meta.env.PRODtrue
import.meta.env.MODE'production'
import.meta.env.SSRfalse(或 true
import.meta.env.BASE_URLstring 字面量

CSS 按需配置位置对照

方案配置文件关键字段状态
Tailwind v4入口 CSS(如 main.css@import "tailwindcss" + @source "..."默认开启 tree-shaking
Tailwind v3tailwind.config.jscontent: [...]当前主流稳定
Tailwind v2tailwind.config.jspurge: [...]已废弃
PurgeCSSpurgecss.config.js 或插件配置content / defaultExtractor独立工具
UnoCSSuno.config.ts自动扫描默认按需

注解 API 速查

注解适用打包器标记对象行为
/*#__PURE__*/Rollup / Webpack / Terser / esbuild(通用)单次函数调用 / 构造 / IIFE该调用未引用返回值时整段可删
/*@__NO_SIDE_EFFECTS__*/Rollup 专属整个函数 / 箭头函数声明一次注解覆盖所有调用点
@__PURE__(无 #)旧形式,部分工具兼容#__PURE__推荐用 #__PURE__

典型用法

ts
const result = /*#__PURE__*/ compute();              // IIFE / 工厂调用
class A extends /*#__PURE__*/ mixin(Base) {}          // class extends 表达式

/*@__NO_SIDE_EFFECTS__*/
function makeStyle(opts) { return compute(opts); }    // 整函数声明(Rollup)

版本与生态状态

工具当前版本Tree Shaking 状态
Webpack5.108usedExports / sideEffects / innerGraph 自 5 起所有 mode 默认开;mode=production 全开
Rollup4treeshake 选项完备;事实标准的 ESM 库打包器
Rolldown(Rust 重写 Rollup)Vite 8 起成为唯一打包器;10–30× 更快
Vite8(2026)Rolldown 替代 Rollup;build.rolldownOptions
esbuild当前内建 tree shaking,极快但选项少
Tailwindv4默认 tree-shaking + CSS-first(@theme / @source
Tailwindv3content 数组扫描类名
PurgeCSS当前跨方案独立工具
package.json sideEffects自 Webpack 4 引入Webpack / Rollup / Vite / esbuild 跨工具行业标准

验证 Tree Shaking 是否生效

Webpack stats 看 usedExports

bash
webpack --json --mode=production > stats.json

stats.json 里搜模块的 usedExports 字段:true = 该导出被使用、false = unused(应被删)。

Bundle 体积对比

bash
# 关闭 sideEffects(基线)
SIDE_EFFECTS=false webpack --mode=production
ls -lh dist/main.js

# 开启 sideEffects
webpack --mode=production
ls -lh dist/main.js

webpack-bundle-analyzer 可视化

bash
webpack --mode=production --analyze

dev 模式验证 tree shaking 无效——开发模式默认 minimize: falseconcatenateModules: false,shaking 标记存在但不删除。必须 production build 后比较。

失效场景速查

场景为何失效解法
CJS 库(lodash / moment)require() 动态改 ESM 等价物(lodash-es / date-fns)
barrel + 缺 sideEffects保守保留整张图sideEffects: false 或深路径 import
sideEffects: false + polyfill误删 polyfill、运行时崩白名单 ["*.css", "./src/polyfills.js"]
try-catch polyfill 检测Rollup tryCatchDeoptimization: true重写为显式 if 或 manualPureFunctions
const isDev = ... 运行期判断失去静态可分析性直接用 import.meta.env.DEV
只标 usedExports 不开 minimize标了不删minimize: true
dev 模式验证dev 不删代码production build
动态拼接 Tailwind 类名扫不到类名完整类名 + safelist
Vite 8 仍写 manualChunks对象移除 / 函数弃用output.codeSplitting

官方资源