Tailwind CSS 的争议很有意思:
一边的人觉得它像“从刀耕火种直接进入工业化”,写界面像搭积木;另一边的人觉得它是“把 CSS 问题转移到了 JSX/HTML 里”,把 className 写成一行《出师表》。
这篇文章我不打算做“站队式安利”——而是把 Tailwind 的优缺点拆开,放在真实项目语境里讨论:什么时候它能帮你省命,什么时候它会让你未来的自己想删库跑路。
Tailwind 的定位很明确:它提供的是低层级 utility classes,让你不用依赖一套“预制组件风格”,而是从零组合出自定义设计(官方介绍就是这个意思)[3]。
同时,它也依赖构建阶段的扫描/清理(purge/JIT)来避免把所有工具类都打进生产包里,从而把最终 CSS 体积控制得很小 [1,2]。
Tailwind 是一种 utility-first(原子化/工具类)CSS 框架:你不再写 .btn-primary { ... },而是在标签上堆“已经定义好的单一职责 class”。
比如一个按钮:
<button
class="rounded bg-blue-500 px-4 py-2 font-semibold text-white hover:bg-blue-600"
>
Submit
</button>你没有写任何自定义 CSS,但这个按钮已经具备颜色、字体、间距、圆角、hover 状态。
这件事的本质不是“少写 CSS”——而是 把样式表达从 stylesheet 转移到 markup。
于是 Tailwind 的优缺点,也几乎都围绕这个转移产生。
传统 CSS 的日常痛点:
.card?.post-card?.postCard?然后团队里出现三种写法Tailwind 的解决方式很粗暴:不命名,直接描述结果。
对于迭代频繁的页面(后台、活动页、MVP),这能明显缩短反馈回路。
不用写 CSS 文件:
export function PostCard({ title, desc }: { title: string; desc: string }) {
return (
<article className="rounded-xl border border-zinc-200 bg-white p-5 shadow-sm transition hover:shadow-md dark:border-zinc-800 dark:bg-zinc-950">
<h3 className="text-lg font-semibold text-zinc-900 dark:text-zinc-50">
{title}
</h3>
<p className="mt-2 text-sm text-zinc-600 dark:text-zinc-400">{desc}</p>
</article>
)
}这里最大的收益不是“少写 CSS”,而是**“样式改动变成组件内的本地改动”**:你改卡片边框,不会意外影响另一个页面的 .card。
Tailwind 的颜色、间距、字号都是一套 scale(比如 p-4、text-lg、zinc-600)。你在项目里推一致性会轻松很多:团队不会动不动写 margin: 13px; 这种“自由发挥”。
更重要的是:你可以把设计语言固化在 tailwind.config.js 里——这点是 Tailwind 特别强调的:它被设计为可配置、可抽象设计系统的引擎 [3]。
// tailwind.config.js
export default {
theme: {
extend: {
colors: {
brand: {
500: '#3B82F6',
600: '#2563EB',
},
},
},
},
}然后你在任何地方都用 bg-brand-600,而不是散落的 #2563EB。
Tailwind 的 md:、hover:、dark: 这类变体,让响应式和交互状态变成一种“前缀语法”,不用写 media query,也不用拆一堆选择器。
<div class="bg-white p-4 hover:shadow md:p-8 dark:bg-zinc-950">...</div>这种“在一个地方看完一个元素的所有状态”,对组件化 UI 非常友好。
很多人第一眼看到 Tailwind 觉得:这么多工具类,CSS 不得上 MB?
但 Tailwind 的工程实践里,核心就是 扫描你用到的 class,并在构建时只生成/保留需要的部分(历史上常被称为 purge/JIT 路线)[1,2]。
这也是它能在大型项目里被实际使用的关键之一:开发期“全量能力”,生产期“按需输出”。
Tailwind 最大的槽点:className 变长,阅读成本上升。
特别是当你开始写这种东西:
<div
class="flex items-center justify-between rounded-lg border border-zinc-200 bg-white px-4 py-3 text-sm font-medium text-zinc-900 shadow-sm hover:bg-zinc-50 md:px-6 dark:border-zinc-800 dark:bg-zinc-950 dark:text-zinc-50 dark:hover:bg-zinc-900"
></div>它并不“难”,但它确实影响可读性,也会增加 code review 的噪音。
怎么缓解:
clsx/tailwind-merge 做条件组合className 常量注意:Tailwind 官方也强调,随着项目变大,把重复模式抽象成更高层组件是合理路径 [3]——这不是“背叛 Tailwind”,而是正确姿势。
Tailwind 的 class 命名很“规律”,但规律本身也是门槛。
新人会经历一个阶段:明明懂 CSS,但不知道 Tailwind 的写法;或者反过来,只会堆 class,不理解布局原理。
在团队里,这会转化成两种风险:
top-[117px]、w-[392px],设计系统被打穿Tailwind 依赖扫描你项目里的 class 来生成 CSS(或清理未用 CSS)[2]。
这会带来一个常见坑:动态拼 class。
例如:
const color = "blue"
return <div className={`text-${color}-500`}>Hello</div>扫描工具可能看不到 text-blue-500,生产环境就没这个样式。
解决方式通常是:
Tailwind 太顺手,导致很多人 UI 一把梭。
短期你会觉得效率爆炸,长期你会发现:
这时候 Tailwind 反而会放大你的组织问题:因为它不给你组件库那种“硬约束”。
Tailwind 的核心价值不是“原子类很潮”,而是它把 CSS 的协作方式换成了:
但它的代价是:如果你不做组件抽象、不做规范约束,Tailwind 会把项目变成大型 className 文物展。
如果你愿意在项目里同时做这三件事——我会说 Tailwind 非常值得:
做到这些,Tailwind 会是那种“越用越顺”的框架;做不到,它也会是那种“越用越乱”的框架。