CSS-in-JS vs 传统CSS:一个成都应届生的样式方案血泪史
去年十月,我刚拿到某大厂的 offer,坐标成都。HR 轻描淡写地说:“月薪 15k,含绩效。”
我心里一咯噔——房租 3500,吃饭 2000,交通 300,再扣掉五险一金和税,到手也就 11k 出头。这在北上广可能连合租都勉强,但在成都,至少还能活得像个人。
入职第一周,导师老张(真名保密)甩给我一个任务:“重构首页组件,用我们现在的样式方案,别整那些花里胡哨的。”
我打开代码库,满屏的 styled-components 和 emotion,心里直打鼓:“这不是传说中的 CSS-in-JS 吗?我在校招面试时才背过概念,实操?零经验啊!”
那个让我通宵的周五晚上
事情真正崩盘是在上周五晚上。
我被安排优化一个商品卡片组件。原本是用原生 CSS 写的,但团队规范要求迁移到 CSS-in-JS。我心想:“不就是把 class 换成 JS 对象嘛,能有多难?”
结果,三小时过去,页面还是白的。
问题出在哪?
- 我用
styled.div包裹了原本的 HTML 结构,但忘了props里传的动态颜色没生效。 - 父组件用
className传样式,子组件却用cssprop,两者根本对不上。 - 更离谱的是,本地跑得好好的,一部署到测试环境,样式全乱了——因为构建工具没配好 CSS 提取插件。
凌晨一点,我瘫在出租屋的破沙发上,泡面凉了,女朋友发来消息:“今天又加班?你上周说好陪我看电影的……”
我回了个“嗯”,然后默默删掉第 8 次提交记录。
那一刻,我真的想砸键盘。
从“CSS 就是写 class”到“原来样式也能编程”
说实话,我本科那会儿学前端,老师教的是最传统的 CSS:
<div class="card">...</div>
.card {
background: white;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
简单、直观、所见即所得。我甚至觉得,“前端不就该这样吗?搞那么多抽象干嘛?”
但进了大厂才发现,现实远比课堂复杂。
我们的项目有上百个微前端子应用,共享一套 Design Token(设计变量)。产品经理今天说“主色换成深蓝”,明天说“按钮圆角调成 4px”。如果还用传统 CSS,光改变量就得搜遍几十个文件,还不提命名冲突、全局污染这些老毛病。
而 CSS-in-JS 的核心优势,就在这儿——样式即逻辑。
比如用 emotion,你可以这么写:
import { css } from '@emotion/react'
const Card = ({ theme }) => (
<div
css={css`
background: ${theme.bgColor};
border-radius: ${theme.borderRadius};
box-shadow: ${theme.shadow};
`}
>
...
</div>
)
看出来了吗?样式直接绑定了 JavaScript 变量。主题切换?传个新 theme 对象就行。不需要维护 .scss 文件里的 $primary-color,也不用担心某个实习生手滑写了 .btn-primary { color: red !important; } 把全局搞崩。
这玩意儿,叫“可编程样式”。
但 CSS-in-JS 真的香吗?我的三个踩坑时刻
别急着吹。作为一个月薪 15k(后来涨到 22k,感谢年终奖)的应届生,我必须说:CSS-in-JS 不是银弹,它是一把双刃剑。
踩坑 1:性能?别开玩笑
第一次代码 Review,老张指着我的 styled(Button) 说:“你这每渲染一次就生成一个新 class,知道 SSR 性能多差吗?”
我懵了。查资料才发现:CSS-in-JS 在服务端渲染时,会为每个组件实例生成唯一 class 名,并内联到 <style> 标签里。用户量一大,HTML 体积暴涨,首屏加载慢得像蜗牛。
解决方案?用 @emotion/cache + extractCritical 提取关键 CSS,或者干脆上运行时缓存。但这些配置,学校谁教过?
踩坑 2:调试?F12 都看不懂
有次线上样式错位,我打开 DevTools,看到的全是这种 class:
<div class="css-1a2b3c4">...</div>
点进去,样式长这样:
.css-1a2b3c4 {
background-color: rgb(255, 255, 255) !important;
}
没有语义、没有层级、没有注释。想找哪个组件写的?只能靠猜。相比之下,传统 CSS 的 .product-card__title--highlight 至少还能看出点门道。
踩坑 3:学习成本?新人直接劝退
我们组来了个实习生,让他改个 hover 效果。他写:
const Button = styled.button`
&:hover {
color: blue;
}
`
结果鼠标移上去没反应。
为什么?因为他父组件用了 React.memo,但 styled.button 每次都返回新组件,导致 memo 失效,hover 状态被重置。
这种“JS 闭包 + CSS 作用域 + React 渲染机制”的混合知识,不是背几个 API 就能搞定的。它要求你同时理解三套体系。
那传统 CSS 就过时了吗?别闹
吐槽归吐槽,我绝不否认传统 CSS 的价值。
最近我接了个内部工具项目,纯后台管理页,功能简单、迭代慢。我直接上 Tailwind + 原生 CSS Modules,开发速度飞快。为什么?
- 无需编译:
.module.css直接 import,class 自动哈希,不怕冲突。 - DevTools 友好:审查元素能看到
.Card_title__abc123,至少知道是 Card 组件的 title。 - 团队上手快:实习生三天就能独立开发,不用先啃一周 emotion 文档。
更别说那些老牌项目——比如公司 2016 年的老后台,还在用 jQuery + 全局 CSS。你敢动?动一下全站崩。这时候,稳定比时髦重要一万倍。
我的选择:没有“最好”,只有“最合适”
经过这半年的折腾,我对样式方案的理解变了。
我不再问“CSS-in-JS 和传统 CSS 哪个更好”,而是问:“这个项目需要什么?”
| 场景 | 推荐方案 | 理由 |
|---|---|---|
| 大型产品、频繁主题切换、强组件化 | CSS-in-JS (如 Emotion) | 动态样式、作用域隔离、与 JS 逻辑深度集成 |
| 内部工具、静态页面、小团队 | Tailwind + CSS Modules | 开发快、调试直观、学习曲线平缓 |
| 遗留系统、低维护需求 | 原生 CSS / SCSS | 稳定、兼容性好、改动风险低 |
上周,我又重构了一个组件。这次我没硬套 CSS-in-JS,而是根据需求拆解:
- 公共布局 → 用 CSS Modules 写基础结构
- 主题相关样式 → 用
useTheme()+ emotion 动态注入 - 交互反馈(hover/active)→ 保留在原生 CSS 伪类里
PR 一次性通过。老张居然夸我:“有进步,知道权衡了。”
代码人生,不止于技术选型
写这篇文的时候,我刚交完房租,3500,押一付三。银行卡余额不多,但心里踏实。
很多人说前端卷,样式方案三天一换,今天 Tailwind 明天 UnoCSS,后天又冒出什么原子化 CSS。作为应届生,很容易焦虑:“我是不是学错了?会不会被淘汰?”
但我想说:技术是工具,人才是核心。
CSS-in-JS 或传统 CSS,本质上都是为了解决“如何高效、可靠地写样式”这个问题。纠结语法不如理解本质——
- 你要解决命名冲突吗?→ 用作用域隔离(CSS Modules / styled-components)
- 你需要动态主题吗?→ 用 JS 变量驱动样式(emotion / vanilla-extract)
- 你追求极致性能吗?→ 用编译时方案(Linaria / Vanilla Extract)
真正的“代码人生”,不是追新,而是知道何时用什么,以及为什么用。
最后一点真心话
如果你和我一样,是个刚入行的应届生,在成都拿着不算高但够活的工资,每天被各种技术方案搞得头大——
别慌。
我去年十月还在为 flex-direction 写反了 debug 两小时,现在也能在 PR 里讨论 CSS-in-JS 的序列化策略了。成长从来不是一蹴而就,而是一次次踩坑、改错、反思堆出来的。
下次当你面对 styled.div 和 .my-class 不知如何选择时,记住:
没有完美的方案,只有更合适的选择。
而判断“合适”的能力,才是我们程序员真正的护城河。
(P.S. 上周终于陪女朋友看了《年会不能停!》,她说我黑眼圈重得像熊猫。看来,是时候优化下自己的“生活样式方案”了。)
教程彩蛋:
想快速上手 CSS-in-JS?别死磕文档。
- 先用
create-react-app起个项目 npm install @emotion/react @emotion/styled- 写一个带
props的按钮,试试动态颜色 - 打开 DevTools,看看生成的 class 长啥样
- 再对比用 CSS Modules 实现同样的效果
亲自动手,比看十篇教程都管用。毕竟,代码人生,干就完了。

评论 0