CSS-in-JS 还是传统 CSS?现代样式方案的选择指南
开篇:为什么我会关心这个“小问题”?

作为一名前端开发者,我每天都在跟样式打交道。从最初的 index.css 到后来的 SCSS、Less,再到现在的 CSS-in-JS,样式的写法可以说是层出不穷。然而,无论技术如何演变,有一件事情从未改变:样式管理一直是前端工程中最容易失控的部分之一。
这篇文章不是为了告诉大家哪种方式“更好”,而是想通过我在几个真实项目中的经历,分享在不同场景下我们该如何选择适合自己的样式方案,并谈谈我个人的一些思考和建议。
问题描述:样式混乱、冲突频繁

几年前我接手了一个中大型 React 项目,是一个内部使用的 CRM 系统。整个项目已经开发了近两年,技术栈是 React + Redux + Material-UI。项目初期采用的是传统的全局 CSS 文件(.css 和 .scss),但随着功能模块越来越多、参与开发的团队也逐渐扩大,样式的问题开始频繁暴露出来:
- 类名冲突严重:比如不同的页面都用了
.btn,但各自定义的 margin、padding、hover 效果却不一致。 - 难以维护:修改某一部分样式时,常常需要在多个文件之间反复查找。
- 复用性差:一些组件样式无法很好地抽离复用,每次都要复制粘贴再调整。
最让我印象深刻的是一次 QA 报告:“点击用户卡片时按钮的颜色变深,但在订单页又恢复正常”。排查半天才发现是因为两个组件使用了同一个 class 名,却分别定义在两个 .scss 文件里,而打包后优先级覆盖了。
那一刻我意识到,传统的 CSS 写法虽然简单直接,但在中大型项目中确实存在很多隐患。我们需要一个更好的解决方案来应对这些问题。
解决方案一:尝试 CSS-in-JS(以 styled-components 为例)
为了解决上述问题,我们决定在新开发的模块中引入 CSS-in-JS 方案。首选的是当时流行的 styled-components。
1. 引入过程
我们并没有马上在整个项目中全面替换,而是先在一个新功能模块中试点 —— 用户行为分析面板(数据可视化组件较多)。
安装依赖非常简单:
npm install styled-components
然后我们就开始用它来写组件样式:
import styled from 'styled-components';
const Card = styled.div`
background: #fff;
border-radius: 8px;
padding: 16px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
`;
const Title = styled.h3`
color: #333;
`;
function UserCard({ user }) {
return (
<Card>
<Title>{user.name}</Title>
</Card>
);
}
这种写法最大的好处就是:样式作用域天然隔离,不需要担心 class 名冲突。而且由于每个组件的样式紧贴其结构逻辑,阅读代码也更直观了。
2. 小插曲:首次上线的样式抖动
不过刚上线的时候我们发现了一个问题:页面加载过程中会出现短暂的“无样式状态”(FOUC),特别是在低网速的环境中。这是因为 styled-components 默认会在运行时注入样式到 <head> 中,而不是像 CSS 文件那样在构建时生成。
解决方法也很简单:
- 使用服务器端渲染(SSR) +
styled-components的ServerStyleSheet; - 或者在客户端项目中配合 Webpack 配置开启提取 CSS 插件(例如
mini-css-extract-plugin)。
但说实话,在非 SSR 的纯前端项目中,CSS-in-JS 对性能还是有一定影响的,尤其是在组件数量庞大的情况下,可能会导致 JS 包体积增大、首屏加载变慢。
3. 更大的收益:提升协作效率
最让我惊喜的是团队协作层面的提升。当多个小组并行开发时,大家再也不需要商量 class 名该怎么命名、也不用互相检查是否会有冲突了。每个人的组件样式都是局部有效的,出了问题只查自己的文件就行。
解决方案二:回到传统 CSS(这次用了 BEM + Tailwind)

不久之后,公司另一个项目启动:一个面向用户的高并发商城网站前端。虽然同样是 React 项目,但我们这次选择了不一样的路线。
这次我们决定继续使用传统的 CSS,但换成了两种工具结合的方式:
- BEM 命名规范:用于自定义组件的样式组织;
- Tailwind CSS:用于快速搭建页面布局和通用 UI 元素。
为什么会做这样的选择?
因为我们对性能要求非常高。商城首页需要秒开、交互流畅,而我们评估后认为使用 CSS-in-JS 可能会增加不必要的 JS 执行负担。同时,设计师已经提供了统一的视觉规范文档,这意味着我们可以更灵活地使用 Tailwind 这样的原子化 CSS 框架。
1. Tailwind 的体验
Tailwind 提供了一种完全不同的写法:不再定义 class 类名,而是直接在 HTML 标签上写样式:
<div className="flex items-center justify-between p-4 bg-white shadow-md rounded-lg">
<span className="text-gray-700 font-medium">商品名称</span>
<button className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600">
加入购物车
</button>
</div>
这种方式一开始有点不适应,尤其对于习惯了语义化 class 的人来说会觉得代码“太碎”。但当你用习惯以后,你会发现:
- 不需要再频繁切换到
.css文件去定义新 class; - 样式调试更快捷,因为可以直接看 HTML 上的 class;
- 可以借助编辑器的智能补全,快速写出想要的效果。
2. BEM 的辅助作用
我们在定制组件(如轮播图、价格对比卡片等)时采用了 BEM 规范,确保结构清晰、命名统一。
比如一个轮播图组件:
<div class="carousel">
<div class="carousel__item carousel__item--active">...</div>
<div class="carousel__item">...</div>
</div>
对应的 CSS 文件则可以精确控制每一个子元素的状态,避免样式扩散。
效果总结:各有千秋,取决于项目特性
经过这两个项目的实践,我得出了一些经验性的结论:
| 特性/方案 | CSS-in-JS(styled-components) | 传统 CSS(BEM + Tailwind) |
|---|---|---|
| 样式封装性 | 极好,组件级作用域 | 需要规范管理 |
| 开发效率 | 快,代码内联 | 快,尤其是 Tailwind |
| 可维护性 | 易定位问题 | 依赖良好的命名规范 |
| 性能表现 | 稍逊色(JS 注入样式) | 更优(提前编译好的 CSS) |
| 学习成本 | 相对较高(需理解动态注入机制) | 较低,标准 CSS + Tailwind 的上手较快 |
| 调试难度 | Chrome DevTools 支持良好 | 同样支持良好 |
对于我们那个 CRM 系统,CSS-in-JS 成功缓解了样式冲突和多人协作问题;而对于那个高流量的商城项目,Tailwind + BEM 的组合带来了更高的性能表现和更高效的页面搭建速度。
经验分享:我的几点建议
结合我这几年踩过的坑和实际经验,给正在纠结样式的你几点建议:
1. 选型前先问自己这几个问题:
- 项目类型是什么?是企业内部后台系统、还是高流量的 C 端网站?
- 团队人员构成如何?是新人多,还是资深工程师主导?
- 是否关注首屏性能?是否需要 SEO 支持?
- 有没有设计规范和 UI 库?是否已有统一的设计语言?
这些都会直接影响最终的技术选型。
2. 别被框架绑架
无论是 styled-components 还是 emotion,或是 Tailwind、Bootstrap,它们都是工具。关键是看它能否帮你更好地完成任务。
有时候我们会过于追求新技术、新潮流,反而忽略了解决问题的本质。
举个真实的例子:有一次我在一个小团队里推广 Tailwind,结果组员反馈“感觉像回到了 inline-style 的年代,很难读”。那我们就果断回归了传统的 SCSS 分离写法,并加强了命名规范的培训。
3. 性能永远值得留意
不管用什么方案,记得关注构建输出大小、加载顺序以及样式树的复杂度。即使是 Tailwind,如果不清除未使用的 CSS,最终打包出来的 CSS 文件也会非常庞大。
可以用 PostCSS 插件 + PurgeCSS 来清理冗余样式:
// postcss.config.js
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
purgecss: {
content: ['./src/**/*.html', './src/**/*.js'],
},
},
};
4. 调试技巧和工具推荐
Chrome DevTools 在样式调试方面做得非常不错,尤其是:
- 右键点击元素 → “Inspect Element”
- 查看 computed style 查看最终样式值
- 使用快捷键
CMD/Ctrl + Shift + M切换设备视图进行响应式调试
另外,VSCode 推荐安装以下插件:
- vscode-styled-components(CSS-in-JS 支持)
- Tailwind CSS IntelliSense(自动补全 Tailwind class)
5. 实践建议:从小处着手,逐步演进
如果你现在正维护一个老项目,或者不想冒然改掉所有样式写法,不妨采取渐进式策略:
- 新增组件使用新的样式方案
- 已有组件按需重构
- 搭建组件库或设计系统统一风格
这样既不会引发项目动荡,也能让团队慢慢接受变化。
结尾:样式没有银弹,只有合适与不合适
回过头来看,这些年我们尝试过各种方式来写样式 —— 从最初的纯 CSS 到后来的预处理器、CSS Modules,再到如今流行的 CSS-in-JS 和 Tailwind,每一种都有它的优势和适用场景。
作为开发者,我们要做的不是一味追随某种流行方案,而是要根据当前项目的实际情况、团队能力和长远目标,做出最适合当下环境的选择。
技术服务于产品,工具服务于团队。
希望这篇文章能帮你在面对“CSS-in-JS vs 传统 CSS”这个问题时,少一点迷茫,多一份从容。
如果还有疑问或者你也有一些实践经验想聊聊,欢迎在评论区留言交流~ 🚀

评论 0