CSS-in-JS 还是传统 CSS?我踩过的坑比你写的样式还多
上周五晚上十一点,我盯着屏幕里一个诡异的样式冲突,差点把咖啡泼到键盘上。又是那个经典问题:某个组件的 padding 被全局样式覆盖了,而这个 Bug 居然在本地跑得好好的,一上线就炸。这已经不是第一次了。作为一名远程办公两年、靠 GitHub Copilot 写代码续命的前端老油条,我终于决定认真梳理一下:到底该用 CSS-in-JS 还是传统 CSS?
说起来,我从 2022 年初就开始用 GitHub Copilot 付费版,快两年了。它帮我写了无数行 styled-components 和 emotion 的代码,也让我在赶项目 deadline 时少掉几根头发。但最近公司准备重构一个老后台系统,技术选型会议上,新来的实习生居然问:“CSS-in-JS 不是过时了吗?” 我当场愣住——这届年轻人,是不是被某些博客带偏了?
一次线上事故引发的思考
事情得从去年双11说起。我们团队负责一个电商管理后台,用了好几年的 Sass + BEM 命名规范。按理说很稳,结果那天凌晨三点,运维突然拉群:“用户列表页样式全乱了!” 我睡眼惺忪地爬起来一看,好家伙,class="user-list__item--active" 被另一个模块的 .list-item.active 覆盖了。
原因?某个同事在写新功能时,为了“快速上线”,直接在全局加了个 .active { color: red !important; }。产品经理催得紧,测试又没覆盖 UI 回归,于是……线上事故。
那一刻我真的想砸电脑。传统 CSS 的作用域问题,又一次把我们坑惨了。
但转头看 CSS-in-JS 呢?也不是银弹。我之前在一个内部工具项目里用 styled-components,结果打包体积暴涨 40KB,Lighthouse 性能分直接掉到 65。更别提 SSR 时的 hydration mismatch 报错,debug 到怀疑人生。
所以,没有绝对正确的选择,只有更适合当前场景的方案。
DeepSeek 时代,样式方案也在进化
最近我用 DeepSeek 看了一些大厂的开源项目,发现趋势挺有意思。比如 Vercel 的 Next.js 官方示例,现在默认推荐用 Tailwind CSS;而 Shopify 的 Hydrogen 框架则坚定拥抱 CSS Modules。Meta 的 React 团队虽然早期力推 stylex(他们内部的 CSS-in-JS 方案),但对外开源项目却多用纯 CSS。
这说明什么?工程化思维正在取代“技术站队”。
我翻了翻 GitHub 上近半年的 star 增长趋势:
| 方案 | 代表库 | GitHub Stars (2023) | 特点 |
|---|---|---|---|
| 传统 CSS | Sass, PostCSS | 稳定增长 | 成熟、工具链完善 |
| CSS Modules | - | 中速增长 | 局部作用域、无运行时 |
| CSS-in-JS | styled-components, emotion | 增速放缓 | 动态样式强、bundle 体积大 |
| Utility-First | Tailwind CSS | 爆发式增长 | 开发效率高、学习曲线陡 |
看到没?Tailwind 的崛起不是偶然。它用原子类解决了传统 CSS 的命名焦虑和作用域问题,又避免了 CSS-in-JS 的运行时开销。难怪越来越多求职者在简历里写“精通 Tailwind”。
性能优化视角:别让样式拖垮你的 LCP
作为一个对性能优化有点执念的人,我专门测过几种方案的首屏加载表现(测试环境:React 18 + Webpack 5 + Chrome DevTools):
- 传统 CSS(Sass):首屏 CSS 体积 28KB,LCP 1.2s
- CSS Modules:首屏 CSS 26KB,LCP 1.1s
- styled-components(客户端渲染):首屏 JS 增加 35KB,LCP 1.8s
- Tailwind(purged):首屏 CSS 19KB,LCP 0.9s
结论很明显:CSS-in-JS 在性能敏感场景(比如 landing page)是劣势项。它的动态注入机制依赖 JavaScript 执行,而浏览器渲染 CSS 是原生高效的。
但!如果你在做高度动态的仪表盘或数据可视化应用,比如根据用户权限实时切换主题色,那 CSS-in-JS 的 props => css 能力简直救命。我之前写一个爬虫监控面板,需要根据任务状态动态改变进度条颜色,用 emotion 三行代码搞定,换成传统 CSS 得写一堆 class 切换逻辑。
我的实战选择策略
经过这几年的折腾,我总结了一套“人话版”选型指南:
选传统 CSS(或预处理器)如果:
- 项目是内容型网站(博客、文档站、营销页)
- 团队有设计师提供 Figma 标注,且设计系统稳定
- 对 bundle 体积极度敏感(比如 PWA 或低端机用户多)
- 你讨厌调试
data-css-hash这种神秘属性
选 CSS-in-JS 如果:
- 应用 UI 高度动态(比如主题切换、实时数据驱动样式)
- 组件库开发,需要强封装性(避免样式泄露)
- 团队全是 React 老手,能接受额外的学习成本
- 你愿意为开发体验牺牲一点性能(且用户设备较新)
选 Tailwind / Utility-First 如果:
- 你追求极致的开发速度
- 设计系统原子化(间距、颜色、字体都有 token)
- 不想纠结命名(告别
btn-primary-large-hover这种噩梦) - 项目周期短,需要快速交付 MVP
Copilot 教我的事:工具只是杠杆
说到这儿,不得不提 GitHub Copilot。它确实能帮我快速生成 styled.button 或 tw 类,但它不会替我做架构决策。有一次我让它写一个 Modal 组件,它默认用了 styled-components,结果我手动改成 CSS Modules + :global() 来兼容第三方图标库。
工具再强,也得人来掌舵。就像我去年面试一个候选人,他滔滔不绝讲 CSS-in-JS 的好处,但当我问“如何处理服务端渲染时的样式一致性”时,他卡住了。技术深度,永远藏在细节里。
最后一点真心话
别被社区的 hype 带着跑。CSS-in-JS 没死,传统 CSS 也没过时。关键是你能不能根据项目上下文做权衡。
我现在的新项目,混合使用了三种方案:
- 全局布局用 Sass(变量 + mixin 复用)
- 业务组件用 CSS Modules(隔离作用域)
- 动态部分用
clsx+ Tailwind 工具类(快速响应状态)
效果?Bundle 体积控制住了,开发效率也没降,线上再也没出现过“神秘样式覆盖”事故。
所以下次再有人问“哪个更好”,你可以笑着回他:“看需求,兄弟。”
毕竟,我们不是在写代码,是在解决问题。而解决问题的人,从来不会被工具绑架。

评论 0