从“写样式”到“管样式”:CSS-in-JS 还是传统 CSS?我在项目中踩过的坑和收获的经验

★罗娟
2025-06-25 13:59
阅读 331

作为一名前端开发工程师,我曾经以为写好样式只是换个颜色、调个 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 不好,而是我们需要更好的组织方式

JavaScript框架对比-1

问题分析:不是 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

最热最新
暂无评论
匿名用户Lv.1
0
影响力
0
文章
0
粉丝