大家好,我又来了😁
最近我接手了一个隔壁组转过来的中后台重构项目。
交接的时候,对方的技术负责人特意跟我强调,说这个项目采用了最新的技术栈,全面拥抱了 Tailwind CSS,开发体验极其丝滑。
我当时心里还挺期待,毕竟这两年 Tailwind 的风刮得太大了,各种国内外大佬都在疯狂带货。结果周末我抽空把代码拉下来,打开 VSCode 准备梳理一下业务主流程。盯了屏幕不到十分钟,我感觉自己的眼睛快瞎了。
光说理论没意思,我直接给你们截取一段真实的、承载了各种表单校验和状态联动的业务侧边栏组件。你们自己品鉴一下所谓的极致开发体验👇:
const OrderCard = ({ order, isAdmin, isExpanded }) => {
return (
<div
className={`flex flex-col w-full p-5 mb-4 border rounded-lg shadow-sm transition-all duration-300 ${
isAdmin ? 'bg-red-50 border-red-200' : 'bg-white border-gray-200'
} ${
isExpanded ? 'max-h-[800px]' : 'max-h-24 overflow-hidden'
} hover:shadow-md cursor-pointer`}
>
<div className='flex items-center justify-between pb-3 mb-3 border-b border-gray-100'>
<span className='text-sm font-semibold text-gray-800 truncate w-[60%]'>
{order.id}
</span>
<span
className={`px-2 py-1 text-xs rounded-full ${
order.status === 'PAID'
? 'bg-green-100 text-green-700'
: 'bg-orange-100 text-orange-700'
}`}
>
{order.statusText}
</span>
</div>
{/* 内部极其复杂的业务字段渲染... */}
</div>
);
}
代码跑得通吗?当然跑得通。UI 还原度高吗?也挺高。 但是作为接下来要维护这个项目的组长,我只觉得一阵头皮发麻。
很多前端新人,甚至是做惯了 C 端 独立开发的兄弟,对 Tailwind 简直是顶礼膜拜。因为它不用取名字,不用在 JS 和 CSS 文件之间来回切换,写起来确实有快感。
但作为趟过无数中后台项目的深水区,我今天必须给这股跟风热潮泼一盆冷水:
在绝大多数重型中后台业务场景里,Tailwind CSS 并不是什么神兵利器,而是给后期维护带来不方便。
来,咱们拿真实代码说话,看看它到底是怎么摧毁中后台工程的👇。
彻底掩盖你的业务主线?
做中后台系统,最难的从来不是画 UI,而是处理极度复杂的数据状态流转。
上面那段代码最大的问题在于信噪比极低。作为一个接手代码的前端,我点开这个文件,首先想看的是:这个组件在不同权限、不同展开状态下,业务逻辑是怎么走的?
但在 Tailwind 的体系下,我的视线全被 flex、p-5、mb-4、max-h-[800px] 这种毫无业务价值的视觉原子类给强暴了。如果退回到老古董的 CSS Modules 方案,这段代码在 JS 侧应该长什么样?咱们对比一下:
import classNames from 'classnames';
import styles from './OrderCard.module.less';
const OrderCard = ({ order, isAdmin, isExpanded }) => {
return (
<div
className={classNames(styles.orderCard, {[styles.adminMode]: isAdmin,
[styles.expanded]: isExpanded
})}
>
<div className={styles.cardHeader}>
<span className={styles.orderId}>{order.id}</span>
<span
className={classNames(styles.statusBadge, {
[styles.statusPaid]: order.status === 'PAID',
[styles.statusPending]: order.status === 'PENDING'
})}
>
{order.statusText}
</span>
</div>
{/* 业务字段一目了然 */}
</div>
);
}
发现区别了吗?重构后的 JSX 变得极其纯粹。我只通过 adminMode、expanded 这种类名,就极其清晰地传达了业务语义。至于那个订单编号到底占百分之几十的宽度,那是 UI 层 该关心的事情,它安静地待在 .less 文件里,绝不会来污染我的业务主逻辑。
跟现成的组件库水土不服
中后台业务是不可能脱离 Ant Design、Element Plus 这类重型组件库的。而组件库的本质,是封装好了一整套内部的 DOM 结构和 ClassName 规范。
这就带来了一个极其致命的冲突:当你用 Tailwind 去覆盖 Ant Design 的内部样式时,你会写出极其恶心的 Hack 代码😖。
比如产品经理要求:在这个特定页面里,把 Ant Design 表格的表头背景色改成浅蓝色,单元格的 padding 改小一点。
正常的 Less 做法是用样式穿透,精确打击:
/* 样式文件覆盖 */
.myCustomTable {
:deep(.ant-table-thead > tr > th) {
background-color: #f0f8ff;
padding: 8px 16px;
}
}
你知道那个拥抱 Tailwind 的小伙子是怎么写的吗?为了不写 CSS 文件,他强行使用了 Tailwind v3+ 的任意变体语法:
// Tailwind 强行覆盖组件库内部样式
<Table
className='[&_.ant-table-thead>tr>th]:bg-blue-50[&_.ant-table-thead>tr>th]:py-2 [&_.ant-table-thead>tr>th]:px-4 [&_.ant-table-tbody>tr>td]:text-gray-600[&_.ant-table-tbody>tr>td]:py-2'
dataSource={data}
columns={columns}
/>
这段代码合进主分支的时候,我都替后续维护的兄弟感到悲哀😢。
这玩意儿连换行都没有,密密麻麻挤在一起。未来如果要做主题切换,或者升级 Ant Design 导致内部类名变了,谁敢去动这坨连正则都极难匹配的字符串?
不仅没有提高开发效率,反而为了强行凑 Tailwind 的语法,写出了一堆极难维护的代码。
负边距引发的问题
一行代码被写出来的成本,远远低于它在未来三年里被维护的成本。
Tailwind 本质上就是披着 ClassName 外衣的行内样式。它把所有的样式固化在了 HTML 结构上。
设想真实的维护场景,前任开发为了让一个按钮和旁边的输入框对齐,极其随意地写了一个向左偏移负间距的类名:
<Button className='-ml-2 mt-1'>提交</Button>
半年后你接手这个需求,产品要求在这俩元素中间加一个 Icon。你看着这个 -ml-2 和 mt-1 会陷入极其痛苦的挣扎:他当时为什么要写负边距?是因为外层父元素加了错的 padding 导致的?还是为了抵消 Button 内部自带的 margin?🤷♂️
在传统 CSS 中,我们往往会留有注释说明抵消输入框自带的右侧留白。但在 Tailwind 里,没有注释的容身之所。
为了保证不出线上 Bug,你绝对不敢删掉那个 -ml-2。你会选择在它后面再打个补丁,加个 pl-4 试图把它顶回来。
第二年,另一个接手的同事遇到了错位,又在后面补了一个 mt-[-5px]🤣🤣🤣。
日积月累,HTML 标签上的类名越来越长,死代码和冲突代码全堆在 DOM 上,最终变成一座没人敢碰的屎山💩。
别瞎搞,先认清你的场景🫡
说了这么多,难道 Tailwind 真的就是垃圾吗?当然绝对不是。
如果你在做偏 C端 的炫酷落地页、做 SaaS 官网,或者你是独立开发者,没有沉重的历史包袱,不需要配合复杂的重型组件库,那 Tailwind 绝对是神作。它自带极其优秀的设计规范,能让你极速堆出好看的界面。
但咱们讨论的是中后台。中后台是干嘛的?团队十几个人来回交接,动辄几百个页面,充斥着极其复杂的表单联动和权限校验,生命周期长达五年甚至十年。
在这种重型项目中,保持业务逻辑的纯粹性,分离关注点,远比你少写几行 CSS 要重要一万倍。
好了,今天分享到这,谢谢大家😁