CSS-in-JS vs 传统CSS:我为什么在项目里做出了这样的选择?

分库分表散人
2025-06-29 14:18
阅读 644

开场白

开场白

记得去年刚加入现在这家公司的时候,我们正要启动一个全新的前端项目。那是一个面向企业用户的内部工具平台,对UI的一致性、组件复用性和可维护性要求都挺高的。

技术选型阶段,团队老大问了个很直接的问题:“这回样式怎么整?继续用传统的 CSS 模块化写法,还是试试像 styled-components 这类 CSS-in-JS 方案?”当时我还挺懵的——在学校和以前实习中主要用的是 SASS 或 CSS Modules,CSS-in-JS 听起来像是“把样式写进 JS”,有点反直觉。

后来经过一番调研和对比,我们选择了 CSS-in-JS 的方案,也踩了不少坑。这篇文章我想结合那次项目经验,聊聊 CSS-in-JS 和传统 CSS 之间的差异,以及在什么场景下更适合使用哪种方式。希望你读完之后,能对这两个方案有更清晰的认识,也能根据自己的项目做出更有信心的选择。


一、问题:为什么我们会被样式困扰?

一、问题:为什么我们会被样式困扰?

项目背景

这个项目的重点在于构建一系列高复用性的 UI 组件库,比如按钮、表格、表单项、弹窗等。这些组件会以 npm 包的形式供多个团队调用,同时需要支持暗色模式切换,并且允许用户通过配置调整主题颜色。

听起来挺常规,但真正做起来才发现:

  • 样式命名冲突是个大问题。虽然我们用了 BEM 的命名规范,但不同项目组引入第三方组件后,button.container 类名还是常常撞车。
  • 主题定制不灵活。想换个配色?得手动改 CSS 变量或者额外引入 SCSS 变量文件。
  • 可维护性差。公共样式散落在各个模块,修改一处经常影响多处。
  • 动态样式的实现不够优雅。比如某个组件的 margin 需要根据 props 来变化,传统 CSS 很难直接反映。

这些问题让我们意识到:传统的 CSS 写法可能已经不能满足当前项目的需求了。


二、尝试改变:为什么我们要用 CSS-in-JS?

二、尝试改变:为什么我们要用 CSS-in-JS?

在一次技术分享会上,我听到一位前辈讲到他们用 styled-components 实现动态样式 + 暗黑模式的案例。我突然想到:如果我们把样式当作变量来管理,很多问题是不是就能解决了?

于是我们决定先做个原型小项目来测试一下。我们选择了 styled-componentsemotion 做对比,最后因为 styled-components 的社区活跃度更高、语法更直观,最终选定它作为主力方案。

关键优势一览:

优点 描述
组件级作用域 不再担心类名重复,每个组件的样式都是独立的
动态样式支持 可以轻松地根据 props 改变样式,不再靠 js 拼接 class
更好的主题支持 使用 ThemeProvider,整个系统的颜色、间距都能通过上下文传递
提升开发效率 样式和结构在一个文件里,重构时不再来回切文件

举个实际例子吧,假设我们有个 Button 组件:

// 使用 styled-components 定义样式
const StyledButton = styled.button`
  background-color: ${(props) => (props.primary ? props.theme.primaryColor : '#fff')};
  color: ${(props) => (props.primary ? '#fff' : props.theme.primaryColor)};
  padding: 12px 24px;
  border-radius: 8px;
`;

// 组件定义
function Button({ primary, children }) {
  return <StyledButton primary={primary}>{children}</StyledButton>;
}

你可以看到,按钮的颜色是根据传入的 primary props 来动态控制的,而主色调又是从全局 theme 中取来的。这样无论你在哪用 Button,只要修改主题配置,所有按钮都会跟着变色 —— 这种体验,在传统 CSS 中真的很难做到这么灵活。


三、挑战与填坑实录:那些我们踩过的坑

三、挑战与填坑实录:那些我们踩过的坑

任何技术都有适用边界,CSS-in-JS 虽然带来了便利,但也带来了一些新的挑战。

1. 初期打包体积问题

一开始我们没注意,发现项目 build 出来的包体积比之前用 CSS Modules 大了不少。查了才知道,是 styled-components 自带的 dev 工具在生产环境中没有被正确剔除掉。

解决方法:

  • 确保部署前运行 process.env.NODE_ENV='production',这样 emotion/styled-components 的 dev 模式代码就不会被打包进去。
  • 使用 Webpack 的 NormalModuleReplacementPlugin 替换调试代码(适用于部分 CSS-in-JS 库)。
// webpack.prod.js
module.exports = {
  plugins: [
    new webpack.DefinePlugin({
      'process.env.NODE_ENV': JSON.stringify('production'),
    }),
  ],
};

2. SSR(服务端渲染)下的样式丢失问题

我们的项目初期并没有用 SSR,但后期接入了 Next.js 做 SEO 优化。这时候发现首次加载时样式没生效,等到 JS 加载完成才“闪现”出来。

原因:

CSS-in-JS 的样式默认是运行时注入 DOM 的,而 SSR 的 HTML 在服务端渲染时并不知道该插入哪些样式。

解决办法:

以 styled-components 为例,我们需要在服务端生成完整的 <style> 标签插入到 HTML 字符串中。官方提供了一个解决方案叫 ServerStyleSheet

import { ServerStyleSheet } from 'styled-components';

export default function renderSSR(Component) {
  const sheet = new ServerStyleSheet();
  const html = renderToString(sheet.collectStyles(<Component />));
  const styles = sheet.getStyleTags(); // 获取所有 style 标签字符串

  return `
    <!DOCTYPE html>
    <html>
      <head>${styles}</head>
      <body>${html}</body>
    </html>
  `;
}

这样页面首次加载的时候就有了正确的样式,用户体验大大提升。

3. 类名太长影响调试?

使用 styled-components 的时候你会发现,类名类似这种:.sc-gqjmRU.eJQZmV,这让浏览器开发者工具看起来不太友好。

我们的折中方案:

  • 开发环境保留默认类名,方便调试。
  • 生产环境开启 displayName 模糊匹配(也可以配合插件简化)。

如果你非常在意这个问题,可以考虑使用 linaria,它会在构建时提前提取出静态样式。


四、回到基础:传统 CSS 什么时候更适合?

虽然我们在那个项目中选择了 CSS-in-JS,但并不是所有场景都适合这么做。我也经历过一些更适合用传统 CSS 的情况。

比如我们有一个基于 Vue 的数据可视化大屏项目,页面结构固定,样式变动极少。那时候我们用的是 SCSS + CSS Modules。

为什么没用 CSS-in-JS?主要原因如下:

  • 数据大屏不需要频繁的主题切换。
  • 图表组件本身依赖 D3.js 控制样式,不需要太多的响应式逻辑。
  • 打包速度很重要,CSS Modules 更轻量。
  • 团队成员普遍对 CSS-in-JS 不熟悉,上手成本高。

在这种情况下,强行引入 CSS-in-JS 反而是加重负担。技术方案一定要服务于业务需求,而不是为了追求新技术而使用。


五、总结:我的几点建议

✅ 适合 CSS-in-JS 的项目特征:

  • 组件复用率高(如组件库)
  • 需要主题系统或高度自定义样式
  • 经常需要动态样式(如根据 props、状态变化)
  • 支持 SSR 并要求首屏样式一致

🚫 不推荐使用的场景:

  • 简单静态页面,样式结构稳定
  • 对首屏性能敏感的小型网站
  • 技术栈老旧,没有配套构建工具
  • 团队协作中新人较多,CSS-in-JS 成本高

🎯 最佳实践 Tips:

  • 如果你决定用 CSS-in-JS,优先使用支持 SSR 的库(如 styled-components、emotion)。
  • 不同库之间迁移成本较高,选型前最好做 POC(概念验证)。
  • CSS-in-JS 不等于“不用写 CSS”,只是换了个地方写而已 😂
  • 善用 Chrome DevTools 的 computed style 查看真实样式,帮助调试。

六、尾声 & 小感悟

回头看这个项目,其实最让我有收获的不是学会了用 styled-components,而是理解了**“工具是为解决问题服务的”**这个道理。很多时候我们会陷入“哪个更好”的争论,却忘了回到最初的目的——写出清晰、易维护、高质量的代码。

就像我在项目中一开始也是带着偏见来看待 CSS-in-JS 的,“把 CSS 写在 JS 里?岂不是违背了分层原则?”但实际用下来才发现,它解决了很多传统方案难以处理的问题。

所以啊,技术从来都不是非此即彼的。关键是要根据业务、团队、架构来合理选择。有时候,最好的答案就是:“视情况而定”。

希望这篇文章对你有所启发!如果你正在纠结 CSS-in-JS 和传统 CSS 怎么选,不妨也动手做一个小实验项目试试看,或许你会有自己的答案。


🚀 本文源码 Demo 地址github.com/you/CSS-in-JS-demo
🔔 有问题欢迎留言,一起探讨现代样式方案的未来方向~

评论 0

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