从“样式混乱”到“模块化管理”:我在项目中对 CSS-in-JS 与传统 CSS 的思考与选择
开篇

我第一次真正意义上面对“CSS 应该怎么写”的问题,是在参与一个中大型 React 项目的重构过程中。那是一个用户量几十万、页面结构复杂且长期由多个团队维护的系统。由于历史原因,样式文件混乱不堪,全局污染严重,组件之间的样式互相影响,维护成本极高。
在这种背景下,我开始尝试各种方案来解决这个问题,其中包括传统的 CSS 模块化(CSS Modules)、BEM 等命名规范,以及后来风头正劲的 CSS-in-JS 方案,比如 styled-components 和 emotion。
这篇文章,我想结合自己的亲身经历,聊聊我在不同场景下如何权衡 CSS-in-JS 和传统 CSS,以及在真实项目中做出的选择和收获的经验。
我们遇到了什么问题?


1. 全局样式污染太严重
在一个老项目中,我们发现某个按钮的颜色总会在某些页面上变成红色,但明明代码里没设置。最后查了半天才发现,是某个第三方 UI 组件偷偷加了个 .btn 的 class,而我们的全局样式也用的是相同的类名。结果就是……按钮变成了红色。
这种情况太多了。每个新功能上线后都伴随着一堆意想不到的样式变化。
2. 样式难以复用,缺乏封装性
每次新增一个组件,都需要新建一个 CSS 文件,然后手动引入。更糟的是,组件之间共享的样式很难统一管理,常常出现重复定义的 margin、padding、颜色变量等。
3. 多人协作时命名冲突频繁
团队人数一多,大家都喜欢自己定义一套 class 命名方式。A 同学写 .card,B 同学也写 .card,C 同学还写了 .Card。最终这些样式全部作用在同一个 DOM 节点上,谁先执行谁赢。
这些问题让我意识到,传统的 CSS 写法虽然简单易懂,但在中大型项目里越来越显得捉襟见肘。
解决思路:引入 CSS-in-JS
为了应对这些问题,我们决定在项目中尝试使用 CSS-in-JS 技术,首选是 styled-components。
一开始大家都有些抵触,觉得把样式写在 JS 里会很怪异。但我提出了几个关键优势:
- 真正的局部作用域:不用担心命名冲突
- 样式可继承、组合性强:可以像写组件一样“封装”样式逻辑
- 动态样式的支持更方便:比如根据 props 动态计算 color、padding 等
- 自动添加厂商前缀:无需再关心浏览器兼容性问题
实践示例
举个实际的例子:
// Button.js
import styled from 'styled-components';
const Button = styled.button`
padding: 10px 20px;
background-color: ${props => props.primary ? '#4CAF50' : '#ccc'};
border-radius: 8px;
color: white;
font-size: 16px;
`;
export default Button;
在这个例子中:
- 我们定义了一个带基础样式的按钮
- 可以通过传入
primaryprop 控制按钮样式 - 所有样式都是局部作用域,不会污染其他组件
这比原来的写法清晰太多了。
使用后的收益和挑战

收益明显:
- 开发效率提升:不用来回切换 HTML/JS/CSS 文件,直接在组件内定义样式。
- 减少了命名焦虑:你再也不用担心
btn,button,Button,my-button到底该用哪个了。 - 更好的样式封装能力:你可以把一组样式逻辑封装成一个基础组件,后续只需调用即可。
- 提高了可维护性:组件样式变更只需要修改对应 JS 文件,不需要找一大堆关联的 CSS 文件。
但也确实存在一些挑战:
- 学习曲线略高:尤其是刚接触的新人需要一段时间适应。
- 调试体验初期不友好:样式类名是随机生成的,刚开始调试时有点懵逼,不过现在大多数 DevTools 插件已经能识别 styled-components 了。
- 构建性能小幅下降:CSS-in-JS 方案在运行时生成样式,首次加载速度略有影响。
- SEO 或 SSR 友好度略差:如果服务端没有正确处理,可能会导致 FOUC(内容闪现)。
为什么不是所有项目都适合 CSS-in-JS?
在我们尝试全面转向 CSS-in-JS 的过程中,我也逐渐意识到,并不是所有项目都适合这种风格。
场景一:小型静态展示页
如果你只是做一个简单的介绍页或营销页,用不到很多交互逻辑,也没有太多组件复用的需求,那传统的 CSS 就足够了。而且它更轻量,构建更快。
场景二:多人协作 + 长期维护的中大型项目
这类项目往往存在以下特点:
- 团队成员流动性大
- 组件复用率高
- 样式结构复杂
- 需要动态控制样式的能力
这个时候 CSS-in-JS 就体现出它的优势来了:模块化强、封装性好、便于组织和管理。
场景三:追求极致性能优化的项目
CSS-in-JS 在运行时会生成 <style> 标签注入文档头部,虽然 emotion 有提取静态样式的能力,但如果非常注重首屏加载速度,传统的 CSS 加 BEM 依然是更稳妥的方式。
最终我们在项目中是怎么做的?
经过几个月的实践和打磨,我们逐步形成了一套混合使用的策略:
对于核心组件库、高频复用组件:
优先使用 CSS-in-JS(如 emotion),实现样式封装和按需加载。
对于布局级组件、通用样式(如字体大小、颜色变量):
依然保留部分传统 CSS(SCSS),配合 CSS Modules 使用。
引入 Tailwind?我们也在探索!
随着 Tailwind CSS 的流行,我们也开始在某些页面快速开发中尝试使用它。尤其是在 Landing Page、表单页等对布局要求高但交互少的页面,Tailwind 提供了极大的便利。
不过对于交互丰富的组件,我们还是更倾向使用 CSS-in-JS 的方式去管理样式,因为它更容易嵌套、组合和条件控制。
我的一些建议和经验分享
✅ 适合尝试 CSS-in-JS 的情况:
- 团队规模 3+,存在多人协作
- 项目周期较长,需要维护
- 组件复用率高
- 样式逻辑比较复杂,需要动态控制
- 已经遇到样式冲突问题
❌ 不建议过度使用的场景:
- 极小的静态页面
- SEO 极其敏感的站点
- 需要极致性能优化(首屏加载)
- 团队对 CSS-in-JS 完全陌生且无培训计划
📌 一些实际开发技巧:
1. 使用 ThemeProvider 实现主题系统
import { ThemeProvider } from 'styled-components';
const theme = {
primaryColor: '#4CAF50',
fontSize: '16px'
};
function App() {
return (
<ThemeProvider theme={theme}>
{/* 你的组件 */}
</ThemeProvider>
);
}
这样可以在任何组件中通过 props.theme.xxx 获取主题变量,非常适合做暗黑模式切换等需求。
2. 样式继承简化写法
const PrimaryButton = styled(Button)`
background-color: #2196F3;
`;
这种方式可以继承已有样式,仅覆盖部分属性,大大减少冗余代码。
3. 与 TypeScript 结合良好
CSS-in-JS + TypeScript 是绝配,可以精准控制 props 类型和样式逻辑,提升类型安全。
4. 注意样式文件过大问题
CSS-in-JS 的样式会被插入到 <head> 中,过多的组件可能导致 <style> 标签过多。这时可以通过 emotion 的插件进行样式抽取和合并。
写在最后
作为一个经历过传统 CSS 时代也拥抱现代工具链的开发者,我的体会是:没有绝对的好坏,只有适不适合。
CSS-in-JS 并不是银弹,也不是必须的。但它确实在组件化、模块化的大趋势下,为我们提供了一种全新的解决方案。尤其在 React 生态中,这种写法几乎成了标配。
如果你的项目也开始面临样式混乱、命名冲突、难以维护的问题,不妨试试 CSS-in-JS,或许它正是你需要的那一剂“良药”。
当然,在使用的过程中一定要关注性能、调试和维护性,不要为了炫技而增加不必要的复杂度。
希望我的这段经历能给你带来一些启发。欢迎留言讨论你们在项目中遇到的样式管理难题,我们一起探讨最佳实践 😊

评论 0