Tailwind CSS 生产环境包体积过大的主要原因,通常是 content 字段配置不完整,或未执行生产构建,导致 JIT 裁剪机制没有生效;必须精准覆盖所有包含 class 的文件路径,并为动态 class 通过 safelist 正则做兜底,同时使用 TAILWIND_MODE=build 验证实际裁剪效果。

原子类未开启 JIT 或按需生成模式,打包后的 CSS 体积会明显膨胀
当 Tailwind、UnoCSS 以全量工具类生成方式构建时,即使 HTML 中只使用了 text-sm 和 flex,最终输出的 CSS 文件依然可能包含全部 10,000+ 个工具类。这并不只是“class 写得多”的问题,而是构建结果里混入了大量根本不会用到的规则,例如 mt-[999px]、bg-gradient-to-tr 这类边缘样式。
带来的影响非常直接:首屏 CSS 体积可能从 80KB 增长到 420KB,LCP 延后 300ms 以上,CI 构建时间还会额外增加 12 秒。很多团队成员按文档执行 npx tailwindcss -i ./src/input.css -o ./dist/output.css,本地看似正常,一上线就出现明显的性能问题。
- 必须在
tailwind.config.js中明确配置content,并准确指向所有包含 class 的文件路径(src/**/*.{js,ts,jsx,tsx,html}) - UnoCSS 用户也要确认
content扫描选项已经启用,同时关闭preflights(除非项目确实需要重置样式) - 如果 Webpack/Vite 插件没有正确注入扫描逻辑,即使写了
content配置,也可能无法真正生效
JS 动态拼接 class 字符串,生产环境样式容易莫名丢失
典型问题场景是:className={`p-${size} ${isDisabled ? 'opacity-50 cursor-not-allowed' : ''}`} 在本地开发没有异常,但上线后 p-4 还在,opacity-50 却消失了——原因在于 PurgeCSS 或内容扫描器无法识别字符串模板中的动态变量值,于是直接将对应样式裁掉。
这不是框架 bug,而是原子化 CSS 的工作机制决定的。Tailwind 这类方案依赖静态分析,一旦通过 JS 动态拼接 class,本质上就是绕过了扫描系统。
- 所有运行时可能出现的 class 名,都应显式包含在
content的 glob 路径中,或通过safelist手动声明(如['opacity-50', 'cursor-not-allowed']) - 尽量避免用数字变量驱动间距类:
mt-${n}比mt-[7px]风险更高,前者可能诱发从mt-0到mt-96的大范围打包 - 无论是 Vue 的
:class,还是 React 中的clsx,都同样受这条规则约束,没有特殊情况
多人同时修改同一段 HTML,class 冲突不会报错但语义会悄悄失效
在不同 PR 中,两个人可能同时给同一个按钮增加 class:PR#123 添加了 bg-blue-600 hover:bg-blue-700,而 PR#125 又叠加了 bg-indigo-600 disabled:bg-gray-300。合并后,按钮禁用态显示灰色没问题,但悬停颜色却看起来失效——原因是 bg-indigo-600 覆盖了 hover:bg-blue-700 的基础色,hover 规则本身仍存在,只是视觉上不再产生变化。
这类冲突通常不会触发 ESLint 报错,在 DevTools 中也只会看到一长串 class,很难第一时间判断究竟是哪条规则被静默覆盖了。
- 建议启用
eslint-plugin-tailwindcss的no-custom-classname规则,限制随意书写非框架类名(如my-btn) - 高频使用的组合应提前沉淀到
@layer components,例如定义.btn-primary { @apply bg-blue-600 hover:bg-blue-700 ... },让多人协作围绕同一个语义入口进行修改 - 关于禁止自由拼装 class 的团队规范,最好同步写进 PR 模板,例如:“新增原子类组合前,请先检查
@layer components中是否已有对应语义类”
设计师调整色值或间距后,前端全局替换往往会漏掉 CSS-in-JS 与内联 style
例如设计系统将主色从 #3B82F6 调整为 #2563EB,前端通过 grep 在全项目中把 blue-500 替换为 blue-600,结果某个 React 组件里的按钮依旧显示旧蓝色——因为那里可能使用了 Emotion 的 css`background: #3B82F6`,或者直接写了 style={{ backgroundColor: '#3B82F6' }}。
原子化 CSS 方案只能管理自身的 class 体系,对其他样式来源并不会自动感知。一旦项目中混用了 Tailwind、CSS-in-JS 和内联样式,后续维护边界就会迅速变得模糊。
- 需要提前约定样式优先级:组件内局部样式 → Tailwind class;跨组件通用状态(如 loading)→ CSS-in-JS 的
styled.div+:is()伪类 - 尽量禁用内联 style,所有动态样式优先通过 class 控制(
className={loading ? 'opacity-50' : ''}) - 在 CI 阶段可通过正则扫描
css`.*#[0-9A-Fa-f]{6}`和style=.*#[0-9A-Fa-f]{6},一旦命中直接 fail
flex 或 gap-4 这样的原子类,而是让团队所有成员都持续重视 JIT、严格维护 content 配置,并接受“不能随意拼接字符串 class”这一约束。最容易被低估的一点是:很多人以为用了 Tailwind 就天然安全、天然高性能,但实际上它只提供工具链,不会自动替你建立协作纪律。