CSS-in-JS 还是传统 CSS?一次真实项目的抉择与反思

梅兰竹菊
2025-06-14 04:02
阅读 258

一、开篇:为什么我要纠结这个问题?

一、开篇:为什么我要纠结这个问题?

去年我参与了一个中型 React 项目,团队原本计划用传统 CSS + Sass 的方式管理样式。但随着组件数量激增、样式冲突频发、主题定制需求越来越多,我们开始频繁地被样式问题拖后腿。特别是当几个并行开发的模块出现样式覆盖和命名混乱时,我才意识到——这不仅仅是写样式的问题,而是整个样式管理体系是否还能支撑业务发展的问题。

那时,我就开始思考一个问题:到底还该不该继续使用传统 CSS 来做现代前端项目?CSS-in-JS 是不是真的可以解决我们遇到的问题?带着这些问题,我决定在下一个版本中尝试引入 CSS-in-JS 方案,并在整个开发过程中持续观察效果。


二、问题描述:传统 CSS 在项目中暴露的痛点

二、问题描述:传统 CSS 在项目中暴露的痛点

我们的项目是一个后台管理系统,涉及大量表格、表单、卡片等 UI 元素。起初使用 BEM 规范写 CSS,配合 Sass 的嵌套语法,结构看起来还算清晰。但随着功能迭代,逐渐暴露出以下几个问题:

  1. 命名冲突严重
    虽然用了 BEM,但不同模块之间还是存在同名类名(如 .btn),导致样式互相覆盖。

  2. 样式复用困难
    比如一个按钮样式,在多个组件中要用到。传统 CSS 要么写多个 .btn-primary, .btn-secondary 等 class,要么复制粘贴代码,缺乏统一抽象机制。

  3. 动态主题支持成本高
    设计部门希望能在运行时切换浅色/深色主题。虽然可以用 CSS 变量实现一部分,但变量组织混乱,维护成本高,最终也没能落地。

  4. 构建体积膨胀
    随着 SCSS 文件增多,编译出的 CSS 也变得臃肿不堪,甚至出现了一些未使用的 CSS(无人敢删)。

这些问题直接影响了开发效率和用户体验。最严重的那次事故是:由于一个公共样式文件被误修改,导致所有页面按钮错位,直到测试环境才被发现。那一刻我意识到,传统 CSS 已经不足以满足这个项目的需求了。


三、解决方案:选择 Emotion 作为 CSS-in-JS 方案

三、解决方案:选择 Emotion 作为 CSS-in-JS 方案

经过对 styled-components、emotion 和 jss 的对比,我们最终选择了 emotion。原因如下:

  • 支持 styled API,写法接近 styled-components,易于上手;
  • 支持 Server Side Rendering(SSR),适合未来可能扩展的方向;
  • 插件生态丰富,可自定义主题系统;
  • 性能表现优秀,官方文档齐全。

我们决定采用 emotion 的 styled 函数来替代传统的类名调用方式,并将一些通用样式封装成“基础组件”。


四、代码实践:从传统 CSS 到 CSS-in-JS 的过渡

传统写法(SCSS):

.card {
  padding: 16px;
  background-color: #fff;
  border-radius: 8px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
}
<div className="card">内容</div>

使用 emotion 后的写法:

import styled from '@emotion/styled';

const Card = styled.div`
  padding: 16px;
  background-color: ${props => props.theme.bg};
  border-radius: 8px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
`;
<Card>内容</Card>

更进一步,我们封装了一套基础样式组件库,比如 Button, Container, Typography 等,供全项目调用。

主题管理也很简单:

// theme.js
export default {
  primaryColor: '#4f46e5',
  bg: '#ffffff',
  dark: {
    bg: '#111827'
  }
};

// App.js
import { ThemeProvider } from 'emotion-theming';
import theme from './theme';

<ThemeProvider theme={theme}>
  <App />
</ThemeProvider>;

五、踩坑经验:那些你以为不会有问题的事,其实很伤人

说实话,在引入 emotion 之前,我以为一切都会很顺利。然而实际开发中还是踩了不少坑:

🐛 动态样式的性能陷阱

有次我在某个循环渲染的列表组件里使用了复杂的动态样式:

<Box color={someExpensiveFn(data)} />

结果每次数据更新,都触发重新计算颜色值,影响了渲染速度。后来优化思路是:

  • 将 expensive 计算提到外层;
  • 或者用 memoization 技术避免重复执行函数;
  • 或者改用 CSS 变量传参。

💣 SSR 渲染不一致

我们在服务端渲染时曾遇到样式丢失的情况。排查发现是某些组件没有正确包裹在 ThemeProvider 中,或某些异步加载的主题数据未及时同步。

解决方案:

  • 确保服务端和客户端都能拿到完整的主题配置;
  • 使用 Webpack DefinePlugin 提前注入主题变量;
  • 对异步数据进行预取或 fallback 处理。

❗ 开发工具的缺失感

虽然 Chrome DevTools 对 CSS-in-JS 的调试支持越来越好,但在初期我们还是觉得缺少那种“直接定位到 CSS 文件”的直观体验。后来安装了 Emotion Devtools 才缓解这个问题。


六、效果总结:项目风格焕然一新

响应式布局概念图-1

迁移完成后,整个项目的变化非常显著:

  • 组件样式更清晰,每一个组件都有自己的样式作用域;
  • 样式冲突几乎消失,因为默认都是局部作用域;
  • 主题切换变得更灵活,只需换一个对象就能全局生效;
  • 构建出来的 CSS 不再是大块静态文件,而是按需注入,大大减少了冗余;
  • 新同学加入后更容易理解每个组件的作用和样式意图。

更重要的是,大家对写样式不再抵触,甚至愿意去封装和重构一些常用组件样式,团队协作效率也提高了不少。


七、经验分享:给正在纠结的你几点建议

✅ 如果你是:

  • 做大型 React / Vue 项目,追求组件化;
  • 需要高度定制化的主题;
  • 经常遇到样式命名冲突、难以复用的问题;
  • 关注性能和可维护性;

那我真的推荐你考虑一下 CSS-in-JS,尤其是像 emotionstyled-components 这样的主流方案。

⚠️ 但如果你:

  • 做的是静态页面、SEO 至上的网站;
  • 项目规模小,不需要太多交互;
  • 团队成员对 JS 更熟悉而对 CSS 较弱;
  • 追求极致的首屏加载速度;

那可能传统 CSS 还更适合你,毕竟 CSS-in-JS 会带来一些 runtime 成本。


八、一点感悟:技术选型的本质是权衡

这次经历让我明白,没有哪种方案是绝对好或坏的,关键是看它适不适合你的项目和团队。有时候我们会迷恋新技术带来的便捷性,却忽略了背后潜在的成本。而在某些场景下,看似“老派”的方法反而更能解决问题。

现在的我,已经不会再纠结“CSS-in-JS 到底好不好”,而是更关注:它是如何让我的项目更稳定、更易维护、更高效地交付价值的。


结语:回到初心,专注解决问题

回过头来看,最初让我们痛苦的并不是 CSS 写法本身,而是如何更好地组织和管理样式逻辑。无论是用 SCSS 加 BEM 还是用 emotion,核心目标都是一致的:写出更健壮、更易维护、更具拓展性的代码。

希望这篇文章能帮你少走些弯路。不管你选择哪种方案,只要能真正解决团队的问题,就是好的实践。

欢迎留言交流你的看法,也欢迎分享你在实际项目中的 CSS 管理经验!

评论 0

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