CSS-in-JS vs 传统CSS:现代样式方案选择指南

QPS追风少年
2025-12-16 20:01
阅读 433

上周五晚上,我正躺在公司附近那个15㎡的出租屋里刷B站,突然钉钉“叮”一声——产品经理发来消息:“明天上线前能不能把主题色切换功能加上?” 我看了一眼时间:23:47。心里一万只草泥马奔腾而过,但嘴上还得回个“好的”。

这事说来也巧。我们组最近在重构一个内部管理后台,技术栈是 React + TypeScript。之前用的是老派 SCSS + CSS Modules,结果现在要支持动态换肤,直接给我整不会了。硬着头皮翻文档、看 GitHub issue、甚至去 Stack Overflow 挖坟,才意识到:样式方案这事儿,真不是写几个 class 就完事的

所以今天这篇,就结合我这个二线互联网公司“三年前端老油条”的实战经验,聊聊 CSS-in-JS 和传统 CSS 到底怎么选。不搞玄学,只讲项目里真实踩过的坑。


起因:一个看似简单的“换肤”需求

我们系统原本只有默认蓝灰配色。产品经理说,运营同学想根据不同节日(比如双11、春节)快速切换 UI 主题。听起来人畜无害对吧?但当我打开 src/styles/variables.scss 那一刻,就知道事情没那么简单。

传统 CSS 的痛点在这类场景下暴露无遗:

  • 所有颜色都写死在 .scss 文件里,没法在 JS 运行时动态改
  • 即使用 :root 定义 CSS 变量,也要手动遍历 DOM 注入 style 标签,维护成本爆炸
  • 更别说有些组件用了内联 style(别问,问就是历史包袱),和全局样式打架

去年双11期间,我就因为类似问题被线上告警 call 起来三次。那次是改了个按钮 hover 效果,结果影响了另一个页面的弹窗动画——CSS 的全局污染,真的会杀人


入坑 CSS-in-JS:从抗拒到真香

说实话,一开始我对 CSS-in-JS 是拒绝的。总觉得“JS 里写 CSS”很反人类,而且听说性能差(后来发现是早期实现的问题)。但被 deadline 逼到墙角,只能硬着头皮试了 Emotion ——毕竟它和 React 生态融合得不错,GitHub star 也够多。

快速上手:三行代码搞定主题切换

装包:

npm install @emotion/react @emotion/styled

App.tsx 顶层包裹 ThemeProvider:

// src/App.tsx
import { ThemeProvider } from '@emotion/react';

const App = () => {
  const [theme, setTheme] = useState('light'); // 或 'dark', 'festival' 等
  
  return (
    <ThemeProvider theme={themes[theme]}>
      {/* 你的组件 */}
    </ThemeProvider>
  );
};

然后在组件里直接消费主题变量:

// src/components/Button.tsx
import styled from '@emotion/styled';

const StyledButton = styled.button`
  background-color: ${props => props.theme.primaryColor};
  border: 1px solid ${props => props.theme.borderColor};
  &:hover {
    opacity: 0.9;
  }
`;

看到没?主题色直接从 JS 对象里取,完全不用碰 CSS 文件。产品经理要加个“圣诞红”主题?我只要在 themes.ts 里新增一个对象,10 分钟搞定。那天晚上 1 点提交代码时,我甚至对着屏幕笑出了声——终于不用再和 SCSS 变量名斗智斗勇了。


但!CSS-in-JS 真的完美吗?

别急着吹。用了三个月后,我在一次性能 review 中被 QA 同事灵魂拷问:“为什么首屏 LCP 比隔壁组慢 300ms?”

查了半天,发现 Emotion 在 SSR(服务端渲染)时会把所有样式 inline 到 HTML 里,导致初始 HTML 体积膨胀。虽然客户端 hydration 后会优化,但对 SEO 和首屏体验还是有影响。

更坑的是浏览器兼容性。我们有个客户还在用 IE11(别问,金融行业你懂的),而 Emotion 默认生成的 CSS 包含 :not(:-webkit-any) 这种 hack,直接让 IE 崩溃。最后不得不加 babel 插件降级,还写了 polyfill。

另外,调试体验也两极分化

  • 好处:VSCode 装了 vscode-styled-components 插件后,语法高亮+智能提示爽到飞起
  • 坏处:Chrome DevTools 里看到的 class 名是 css-1a2b3c4 这种 hash,排查样式覆盖问题时得靠猜(虽然可以配 labelFormat,但团队里没人记得开)

回归理性:什么场景该用哪种方案?

经过这轮折腾,我和组长开了个技术复盘会,总结出一套“决策树”。分享给各位兄弟,避免重蹈我的覆辙:

场景 推荐方案 理由
高度动态 UI(如主题切换、运行时配色) CSS-in-JS (Emotion / styled-components) JS 直接控制样式,逻辑集中
静态营销页 / 内容型网站 传统 CSS (SCSS + CSS Modules) 无状态,打包后体积小,SEO 友好
微前端架构 / 多团队协作 CSS Modules + BEM 命名 避免样式冲突,边界清晰
需要极致性能(如电商首页) 传统 CSS + Critical CSS 内联 减少 JS 解析开销,首屏更快
组件库开发 CSS-in-JS + 传统 CSS 双输出 既支持动态定制,又保留静态引用能力

💡 我们现在的策略:核心业务用 Emotion,静态页面用 SCSS。通过 Webpack 配置按需加载,互不影响。


给新手的建议:别为了新而新

刚入行那会儿,我也迷信“新技术一定更好”。结果在第一个项目里强行上 styled-components,结果 build 时间翻倍,还因为没处理好 SSR 被运维大哥追着骂。

现在带实习生,我第一句话就是:“先搞清楚需求,再选工具”

如果你的项目只是做个博客或者后台表格,传统 CSS 完全够用,别给自己加戏。CSS Modules 配合 BEM 命名,已经能解决 90% 的作用域问题。而且团队里非前端同事(比如切图的设计师)也能看懂 .button--primary 是啥意思,沟通成本低。

但如果你在做 Design System、需要支持深色模式、或者用户能自定义 UI,那 CSS-in-JS 的优势就压倒性了。关键是把样式当作“可编程的数据”,而不是“静态资源”


最后:工具没有银弹,只有合适

写这篇文章时,窗外上海的梅雨季又开始了。想起三年前我刚入职这家公司,连 Flex 布局都要查 MDN。现在虽然能侃侃而谈 CSS-in-JS 的优劣,但每次遇到样式 bug,还是会默默打开 Chrome DevTools 的 Styles 面板,一行行勾选排查。

技术选型这事儿,从来不是非黑即白。CSS-in-JS 不是银弹,传统 CSS 也没过时。重要的是理解背后的设计哲学:前者追求“逻辑与样式的统一”,后者坚持“关注点分离”。

下次当产品经理再半夜找你改样式,别慌。先问清楚需求本质,再决定是打开 styles.scss 还是写个 styled.div。毕竟,我们前端的终极目标,不是炫技,而是——早点下班

(完)

P.S. 如果你也在用 Emotion,记得开启 sourceMapautoLabel,调试时能救命。
P.P.S. 租房合同快到期了,求推荐浦东靠谱小区…预算 6k 以内,离地铁近就行 😭

评论 0

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