技术文章
在县城远程办公三年,我悟透了前端样式方案的选型逻辑
凌晨两点半,县城的街道上连个鬼影都没有,只有偶尔几声土狗的狂吠。我戴着降噪耳机,里面正播放着用 ElevenLabs 合成的女声版“前端高级面经”。别说,这 AI 声音是真自然,连换气声和轻微的停顿都有,就是读到“React Fiber 架构”和“CSS 渲染流水线”的时候偶尔会卡壳,听得我直犯困。
作为一个在县城远程办公了三年的“小镇做题家”,我的生活基本就是两点一线:早上九点准时打卡进公司的钉钉群,晚上等需求上线后继续刷 LeetCode 准备跳槽。我的 VSCode 里塞满了各种花里胡哨的插件,最近还跟风装了 Cline,想着让 AI 帮我写写 CRUD 业务代码,好腾出时间背八股文。
但说实话,AI 写业务逻辑还行,一碰到复杂的样式微调,它生成的代码经常让我血压飙升。这就不得不提到前端界那个永恒的争论:CSS-in-JS 和传统 CSS,到底该怎么选?今天刚改完一个老项目的祖传样式,趁着等运维发版的空档,我决定把这几年踩过的坑和心得盘一盘。
被样式冲突支配的恐惧
讲真,我对样式方案的执念,完全是被现实毒打出来的。
去年双十一前夕,我们那个外包性质的内部营销系统要做大改版。当时的项目用的是最传统的 SCSS,加上所谓的 BEM 命名规范。听起来挺正规对吧?但现实是,接手这个项目的老哥早就提桶跑路了,留下的代码简直是一座“屎山”。
那天晚上快十一点,产品突然在微信群里@我,说:“那个活动页的红色按钮,能不能改成渐变色,而且 hover 的时候要有个呼吸灯效果?对了,顺便把旁边那个列表的间距调小 2px,老板明天一早要看。”
我打开 Chrome DevTools 一查,好家伙,那个按钮的类名是 .btn-red,但在全局样式文件里,.btn-red 居然被复用了七八次!我改了一个地方的背景色,结果导致另一个页面的提交按钮也变成了渐变色。当时我真的想砸电脑,为了修这个 Bug,我硬是熬到凌晨三点,给所有类名加上了冗长的业务前缀,才勉强把线上事故压下去。
那次事故后,我就开始疯狂关注样式隔离和工程化的方案。也就是在那时候,为了面试能多吹点牛,CSS-in-JS 进入了我的视野。
折腾 CSS-in-JS:看着很美,用着很痛
准备跳槽嘛,总得搞点新技术包装一下简历。CSS-in-JS 里的 Styled-components 和 Emotion 当时风很大,号称“用写 JS 的方式写 CSS”,彻底解决样式冲突,还能动态传参。
我兴冲冲地在新接的一个 SaaS 后台项目里引入了 Styled-components。刚开始那几天,体验简直起飞。
import styled from 'styled-components';
const Button = styled.button`
background: ${props => props.primary ? '#1890ff' : '#fff'};
color: ${props => props.primary ? '#fff' : '#333'};
padding: 8px 16px;
border-radius: 4px;
border: 1px solid ${props => props.primary ? '#1890ff' : '#d9d9d9'};
&:hover {
opacity: 0.85;
}
`;
你看,这代码多优雅,逻辑和样式完全内聚,再也不用担心命名冲突了,连 Cline 帮我生成组件代码时,都能直接吐出这种高内聚的格式。
但好景不长,随着项目越来越大,坑开始一个个爆出来。
第一个坑是运行时性能损耗。CSS-in-JS 是在浏览器运行时把 JS 对象解析成 CSS 字符串,然后动态注入到 <style> 标签里的。我们那个后台系统有几百个表格和表单,首屏加载时,JS 引擎疯狂计算样式,导致 LCP(最大内容绘制)指标直接飙到了 3.5 秒以上。产品跑来问我:“为什么这页面打开这么卡?是不是你代码写得烂?”我当时只能含糊其辞,说是网络问题。
第二个坑是调试体验极差。你在 DevTools 里审查元素,看到的类名全是 sc-bdVaJa cHxLqR 这种哈希值。想找对应的样式?得靠 SourceMap,但有时候 SourceMap 加载不出来,你就只能在一堆乱码里玩“找茬”游戏。有次为了找一个 1px 的边框问题,我盯着屏幕找了半个小时,眼睛都花了。
第三个坑是包体积和 SSR 水合报错。为了支持动态样式,库本身就不小。后来我们尝试做 SSR 优化,结果因为服务端和客户端生成的哈希值不一致,导致 React 报了一堆 Hydration Mismatch 的警告,满屏的红字看得我心惊肉跳。
回归理性:传统 CSS 的现代化复兴
被 CSS-in-JS 毒打了一顿后,我冷静了下来。工作嘛,毕竟不是在学校里做 Demo,稳定性和性能才是第一位的。我开始重新审视那些看似“传统”的方案,发现它们其实早就进化了。
1. CSS Modules:稳如老狗的隔离方案
如果你用的是 Create React App 或者 Vite,CSS Modules 真的是最省心的选择。它不需要任何额外的运行时开销,纯粹在构建阶段通过 Webpack 或 Vite 插件把类名加上哈希后缀。
/* Button.module.css */
.primaryBtn {
background: #1890ff;
color: #fff;
}
import styles from './Button.module.css';
function Button() {
return <button className={styles.primaryBtn}>Click me</button>;
}
没有运行时性能损耗,没有 SSR 水合问题,类名虽然也是哈希,但在 DevTools 里配合 SourceMap 调试非常顺滑。唯一的缺点就是写动态样式稍微麻烦点,得用内联 style 或者 CSS 变量来中转。
2. Tailwind CSS:原子化 CSS 的降维打击
如果说 CSS Modules 是防守,那 Tailwind CSS 就是进攻。这玩意儿我刚接触时是拒绝的,觉得在 HTML 里写一堆类名简直是“反人类”。但用了一周后,我只想说:真香。
Tailwind 的 JIT(Just-In-Time)编译器太牛了,你写什么类名,它就生成什么 CSS,最终打包出来的样式文件只有几 KB。而且它彻底治好了我的“命名困难症”,再也不用想 .btn-primary-large 这种破名字了。
<button class="bg-blue-500 hover:bg-blue-600 text-white font-bold py-2 px-4 rounded">
Submit
</button>
配合 VSCode 的 Tailwind CSS IntelliSense 插件,写起来简直如丝般顺滑。而且它和组件库结合得非常好,我们现在的项目里,基础组件用 Tailwind 写,复杂业务逻辑用 CSS Modules 兜底,简直是绝配。
3. 原生 CSS 变量与嵌套:浏览器原生真香
别忘了,CSS 本身也在进化。现在浏览器对 CSS 变量(Custom Properties)和原生嵌套的支持已经非常好了。
:root {
--primary-color: #1890ff;
--spacing-md: 16px;
}
.card {
padding: var(--spacing-md);
& .title {
color: var(--primary-color);
font-size: 1.2rem;
}
}
用 CSS 变量来做主题切换和动态样式,性能比 JS 计算好一万倍。而且原生嵌套语法让 CSS 的结构更清晰,再也不用写一堆扁平化的选择器了。
硬核对比:到底该怎么选?
为了让大家看得更直观,我整理了一个对比表格。这可是我拿自己项目实测出来的数据,绝对干货。
| 方案 | 运行时性能 | 包体积影响 | 动态样式支持 | 调试体验 | SSR 支持 | 学习成本 | 推荐场景 |
|---|---|---|---|---|---|---|---|
| Styled-components | 差 (有 JS 计算开销) | 较大 (需引入库) | 极佳 (直接传 props) | 差 (哈希类名) | 需额外配置 | 中 | 小型项目、对性能不敏感的后台 |
| Emotion | 中 (支持字符串模板) | 中等 | 极佳 | 中 | 需额外配置 | 中 | 需要极致灵活性的组件库开发 |
| CSS Modules | 极佳 (无运行时) | 无影响 | 一般 (需借助变量) | 佳 (配合 SourceMap) | 完美 | 低 | 中大型项目、追求稳定的业务线 |
| Tailwind CSS | 极佳 (JIT 编译) | 极小 (按需生成) | 中 (需结合任意值) | 佳 (类名语义化) | 完美 | 中高 | 快速迭代的项目、C端页面、重构 |
| 传统 SCSS/LESS | 极佳 | 无影响 | 差 | 中 (易冲突) | 完美 | 低 | 老项目维护、极小规模的页面 |
我的选型心法:不要为了技术而技术
在县城远程办公这几年,我最大的感悟就是:技术是为业务服务的,不要为了炫技而引入复杂的方案。
如果你是在做从 0 到 1 的新项目,且团队对 Tailwind 接受度高,无脑上 Tailwind CSS。它的开发效率提升是实打实的,而且后期维护成本极低。
如果你是在维护一个老项目,或者团队里有不少新手,CSS Modules 是最稳妥的选择。它不会改变大家写 CSS 的习惯,又能完美解决样式冲突问题。
至于 CSS-in-JS,除非你是在开发一个像 Ant Design 那样需要高度动态化、支持主题定制的开源组件库,否则在业务项目里我真的不推荐。它的运行时开销和调试成本,在业务跑起来后会成为巨大的隐患。
写在最后
耳机里的面经音频刚好播完,ElevenLabs 合成的女声说了句“祝您面试顺利”。我摘下耳机,揉了揉发酸的眼睛,看了一眼屏幕右下角,已经凌晨四点了。
VSCode 里的 Cline 插件还在后台默默跑着,帮我生成着明天要用的几个表单组件。我端起保温杯,喝了一口泡得发白的枸杞水,把刚才总结的样式选型方案保存到了我的 Notion 笔记里。
其实吧,不管是 CSS-in-JS 还是传统 CSS,没有绝对的好坏,只有适不适合。就像我们这群小镇做题家,没有大厂的光环,没有顶会的论文,只能靠着一个个深夜的死磕,一行行代码的积累,在这条路上慢慢往前挪。
明天还要跟产品对需求,还得继续刷两道动态规划。不说了,我得赶紧去洗个澡,睡三个小时后还得起来打卡呢。希望明年的这个时候,我能拿着这份对前端工程化的深刻理解,顺利跳槽去个一二线城市的大厂吧。
各位同行,共勉。如果你们在样式选型上有什么坑,欢迎在评论区吐槽,让我平衡一下。

评论 0