从“写样式”到“管样式”:CSS-in-JS 还是传统 CSS?我在项目中踩过的坑和收获的经验
作为一名前端开发工程师,我曾经以为写好样式只是换个颜色、调个 margin 的小事。但随着项目体量的增加、团队协作的复杂化以及用户对体验要求的提升,我发现样式管理这件事远没有表面看起来那么简单。
今天这篇文章我想结合自己亲身经历过的一个 React 大型中后台系统的开发过程,分享一下我在使用 CSS-in-JS(以 styled-components 为例)与 传统 CSS(包括 SCSS 和 CSS Modules)之间做取舍的真实经历。希望可以帮你在面对同样问题时,做出更合适的决策。
背景介绍:从一个看似简单的登录页开始

事情还得从我们团队接手一个全新的企业级 SaaS 产品说起。项目一开始是个比较典型的 React + TypeScript 技术栈,整体采用组件驱动的开发模式。最开始我们选择用传统的 CSS Modules 方式来处理组件样式。
当时的想法很简单:组件拆得足够细,样式独立打包,命名不会冲突,应该没问题。
结果上线前两个月,我们在迭代中遇到了一系列“样式噩梦”,比如:
- 某个组件修改样式后,其他页面也跟着变了(明明用了 modules 啊!)
- 样式类名在浏览器里全是
.a_b_c_d__title这种可读性极差的名字,调试起来很痛苦 - 主题切换需求来了之后,想改主色调,居然要遍历所有组件文件找 color 值
- 团队新成员上手慢,因为不知道样式怎么组织、怎么继承
这些问题逐渐暴露出传统 CSS 管理方式在现代大型项目中的局限性。
问题分析:不是 CSS 不好,而是我们需要更好的组织方式


这时候我开始思考:CSS 本身并没有错,它仍然是构建网页样式的基石。真正的问题在于,如何在一个日益复杂的前端应用中,更好地组织、封装和复用样式代码?
我们尝试了几种主流方案:
✅ CSS Modules:解决了命名冲突,但依然有维护成本
- 优点:模块化、作用域隔离、打包工具支持好
- 缺点:
- 类名不直观,难以调试
- 公共变量或混合需要引入 SCSS 或 PostCSS
- 难以动态更改样式(如响应主题变化)
🤔 BEM 写法 + 工具辅助:结构清晰但仍然耦合组件逻辑
- 适合 HTML/CSS 单人小项目或静态页面
- 在 React 中与组件结构分离,不利于组件复用和状态控制
💡 CSS-in-JS(styled-components):真正做到了“组件即样式容器”
我们最终决定在一部分新功能模块中引入 styled-components 来试水,结果出乎意料地顺畅:
- 组件样式写在组件内部,结构清晰
- 支持通过 props 动态修改样式
- 可直接使用 JS 逻辑控制样式,不再依赖额外预处理器
- 支持嵌套写法,符合开发者直觉
实施过程:一次重构引发的“连锁反应”

举个具体的例子吧:我们要做一个权限配置的卡片组件,每张卡片根据用户角色显示不同的边框颜色和背景色。这个需求如果用传统 CSS 怎么实现呢?
<Card className={userRole === 'admin' ? styles.adminCard : styles.defaultCard} />
然后每个类都要提前定义在 CSS 文件中。而用 styled-components,我们可以直接写成这样:
const RoleCard = styled.div`
padding: 1rem;
border-radius: 8px;
background-color: ${props => props.isAdmin ? '#f0f9ff' : '#fff'};
border: 2px solid ${props => props.isAdmin ? '#3b82f6' : '#e4e4e4'};
`;
<RoleCard isAdmin={user.isAdmin}>
{/* card content */}
</RoleCard>
是不是一眼就能看出风格差异?而且这种写法天然支持响应式和状态切换,不需要再引入一堆媒体查询或者写条件判断类名了。
另外,在调试时,你可以在浏览器 DevTools 中看到组件名称,而不是一串 hash 出来的类名,这对排查问题帮助极大。
当然,CSS-in-JS 并不是没有缺点。我们在实践中也遇到了几个挑战:
真实遇到的坑和解决方案

1. 初期性能担忧 vs 实际表现良好
我们担心大量使用 styled-components 是否会影响页面渲染性能,尤其是在移动端。经过 Lighthouse 测试后发现:
- 对于大多数中后台系统来说,样式注入带来的性能开销几乎可以忽略不计
- 我们优化了 SSR 支持,避免首次加载闪动(FOUT)
- 使用
@emotion/css替代某些高频组件的样式注入,减少运行时负担
2. 样式共享困难?试试 ThemeProvider!
当我们需要全局控制主题变量(如主色值、字体大小等)时,发现 styled-components 提供了一个非常方便的上下文机制 —— ThemeProvider。我们把设计系统的变量统一放到 theme 对象中,整个项目都能访问:
<ThemeProvider theme={{
primaryColor: '#3b82f6',
fontSize: '16px'
}}>
<App />
</ThemeProvider>
然后在任意组件中都可以使用 ${props => props.theme.primaryColor}。
3. 调试工具加持:Styled Components DevTools 插件太香了!
Chrome 插件 Styled Components DevTools 简直是我的救命神器,可以直接在元素面板中看到对应的 styled 组件定义位置,大大节省了调试时间。
最终效果:更高效,更可控,更容易维护
引入 styled-components 后,我们的开发效率提升了不止一点点:
| 指标 | 传统 CSS | CSS-in-JS |
|---|---|---|
| 新人学习成本 | 较高(需掌握命名规范、文件结构) | 中等(只需会 JS+CSS) |
| 样式复用率 | 低(容易复制粘贴) | 高(可封装成基础组件) |
| 主题变更成本 | 高(需全局搜索替换) | 低(theme 控制) |
| 开发调试体验 | 一般(类名难读) | 优秀(组件名友好) |
最重要的是,我们终于实现了“样式即组件”的理念 —— 每个 UI 组件都自包含其样式逻辑,不再担心副作用或污染全局样式。
给你的建议:选对工具,才能事半功倍
说了这么多,我并不是在鼓吹 “CSS-in-JS 就是唯一正确的答案”。其实,在实际工作中,两者完全可以共存甚至互补。
我的建议如下:
✅ 推荐使用 CSS-in-JS 的场景:
- React/Vue 项目,特别是组件粒度细、样式多变的 SPA
- 需要频繁换肤/多主题支持
- 追求开发效率和调试体验
- 团队技术栈偏 JS 体系,不太熟悉 CSS 预处理器
✅ 传统 CSS 仍有价值的地方:
- 大型网站或 SSR 优先的项目,注重首屏性能和 SEO
- 已有老项目迁移成本太高,不适合大动干戈
- 设计系统成熟,样式相对固定
💡 我的小经验分享:
- 如果你已经用上了 Tailwind 或 Windi,其实也可以搭配 CSS-in-JS,各取所长
- 为不同项目制定不同的样式策略,不要一刀切
- 学会善用 Chrome 的样式调试器,不管哪种写法都是必备技能
- 不要盲目追求“炫技”,能解决问题的就是好技术
结语:写样式不只是视觉工作,更是工程能力的体现
回想这次从传统 CSS 转向 CSS-in-JS 的过程,对我来说不只是技术选型的变化,更是思维方式的转变。从前我觉得写样式就是让东西“长得好看”,现在我更关注它是“如何组织的”、“能不能复用”、“好不好维护”。
前端发展这么快,新的方案层出不穷。但归根结底,还是要服务于产品目标、团队协作和个人体验。选择合适的技术,才是真正的功力所在。
希望你能从我的这段经历中获得一些灵感,少走弯路。如果你也在用 CSS-in-JS,或者正在纠结要不要尝试,欢迎留言交流,我们一起聊聊你的项目故事~

评论 0