我为什么认为 CSS-in-JS 是一个失败的技术?

在2025年的今天,我想说一句可能会被很多同行喷的话🤯:

我个人认为,以styled-componentsEmotion为代表的、在运行时注入样式的CSS-in-JS技术,从长远来看,是一项失败的技术。

我知道,这个观点很暴论。

在它最火的那几年(大概2018-2022),我也是它的忠实拥护者。我们团队的好几个核心项目,都深度使用了styled-components。它解决的局部作用域、基于状态的动态样式等问题,在当时确实是前端开发的巨大痛点。

但技术是在演进的。当初觉得是新技术,在今天看来,可能已经变成的很Low。

这篇文章,就是我想复盘一下,我们当初为什么爱上了它,又为什么在我们的新项目中,最终决定彻底放弃它。


我们当初为什么选择了它?

要客观地评价一项技术,首先要承认它的价值。CSS-in-JS在那个时代,确实解决了几个CSS开发的核心难题。

1. 完美的局部作用域 在CSS-in-JS出现之前,为了避免CSS类名全局冲突,我们发明了BEM这样的命名规范,或者使用CSS Modules。但它们要么依赖开发者的自觉,要么需要额外的构建配置。而CSS-in-JS通过自动生成唯一的类名,从根本上解决了这个问题,让我们可以毫无心智负担地给组件写样式。

2. 与组件状态的结合 这是它最吸引人的地方。我们可以非常优雅地,把组件的props直接传递给样式,实现动态CSS。

jsx 复制代码
// 这种写法,在当时看来,简直是天才般的优雅
import styled from 'styled-components';

const Button = styled.button`
  background: ${props => props.primary ? 'palevioletred' : 'white'};
  color: ${props => props.primary ? 'white' : 'palevioletred'};
  font-size: 1em;
  margin: 1em;
  padding: 0.25em 1em;
  border: 2px solid palevioletred;
  border-radius: 3px;
`;

// 使用
<Button>Normal</Button>
<Button primary>Primary</Button>

对比当时需要用className拼接或者操作内联style的繁琐,这种体验是降维打击。


那些我们无法再忍受的代价

当我们的项目越来越复杂,用户对性能的要求越来越高,CSS-in-JS的代价开始逐渐显现,并最终让我们无法忍受。

运行时性能损耗(The Performance Tax) 这是最致命的一点。

你的浏览器,在渲染页面时,不仅要解析和执行你的业务逻辑JS,还要额外花费时间和CPU,去解析你的样式JS,把它序列化成CSS字符串,然后再创建一个<style>标签,动态地插入到DOM的<head>里。

这个过程,在组件第一次渲染时(mount)都会发生。当页面上有大量动态组件时,这个运行时的开销,会显著地拖慢你的页面首次渲染速度(FCP)和可交互时间(TTI)。

除此之外,CSS-in-JS的运行时库本身,也增加了我们最终的打包体积。为了解决一个CSS的问题,却让我们的JS背上了更重的负担,这在性能优化的视角下,有点本末倒置。

与CSS生态的割裂 CSS-in-JS,名义上是CSS,但实际上,它让你脱离了整个CSS的生态系统。

  • 工具链的割裂:很多强大的CSS静态分析工具、PostCSS插件,都无法处理你写在JS模板字符串里的伪CSS。
  • 浏览器优化的割裂 :浏览器对原生CSS文件,有一套非常成熟的解析、渲染和缓存优化机制。而对于动态插入的<style>标签,浏览器能做的优化就非常有限了。比如,它无法在HTML解析阶段就并行下载和解析CSSOM。

不必要的复杂性 我们为了解决局部作用域和动态样式这两个问题,引入了一个庞大的运行时、一套新的语法(模板字符串里的CSS)、以及一套复杂的SSR(服务端渲染)方案。

这是一个典型的用牛刀杀鸡的方案。它引入的工程复杂性,远远超过了它解决的问题本身。


2025年,我们有了更好的选择

技术总是在发展的。当初我们选择CSS-in-JS的那些痛点,在2025年的今天,大部分已经被浏览器和现代工具链,用更优雅、代价更小的方式解决了。

  • 针对局部作用域CSS Modules 我个人认为 依然是一个非常好的选择。它在构建时就完成了类名的哈希化,没有任何运行时开销。

  • 针对动态样式CSS自定义属性(CSS Variables) 已经成为了所有现代浏览器的标配,它的能力远比我们想象的强大。我们可以轻松地把上面那个按钮的例子,用CSS Variables来改写:

    css 复制代码
    /* button.module.css */
    .button {
      background: var(--button-bg, white);
      color: var(--button-text, palevioletred);
      /* ...其他样式 */
    }
    jsx 复制代码
    // Button.jsx
    import styles from './button.module.css';
    
    function Button({ primary, children }) {
      // 通过内联style来设置CSS变量的值
      const style = primary ? {
        '--button-bg': 'palevioletred',
        '--button-text': 'white'
      } : {};
      
      return <button className={styles.button} style={style}>{children}</button>;
    }

    这种方式,既保留了动态能力,又没有任何运行时JS的性能损耗。

  • 其它新的轮子: 社区也意识到了运行时CSS-in-JS的问题,催生了 零运行时的CSS-in-JS 方案,比如Linaria, Panda CSS。它们允许你用类似styled-components的语法来写样式,但在构建时,会把所有的样式都提取成静态的.css文件,完美地解决了性能问题。


所以,我为什么说(运行时)CSS-in-JS是一项失败的技术?

它没有失败在它的解决思路上------它的思想在当时是革命性的。它失败在我们付出的代价上上。在2025年,我们有了太多开发代价更低、同样能解决问题的方案。

回顾它的兴衰,我作为一个亲历者,觉得它更像是一次有价值的坑。它用一种激进的方式,暴露了原生CSS的种种痛点,并倒逼CSS标准和前端社区,去寻找更好的、性能更高的解决方案。

作为WEB前端工程师,我们的工作就是不断地做权衡。放弃一个曾经深爱的技术,不是一种背叛,而是一种成长。

这是我从CSS-in-JS 实战中学到的宝贵经验🙂。

相关推荐
月下点灯2 小时前
✨项目上线后产品要求把应用字体改大点📏怎么办?一招教你快速解决🔧
前端·vite
xvmingjiang2 小时前
Vue 3 中监听多个数据变化的几种方法
前端·javascript·vue.js
我有一只臭臭2 小时前
ES5 和 ES6 类的实现
前端·javascript·es6
excel2 小时前
Three.js 实现高分辨率地球边界可视化
前端
LaoZhangAI2 小时前
Google Gemini AI图片编辑完全指南:50+中英对照提示词与批量处理教程(2025年9月)
前端·后端
用户11481867894842 小时前
从零搭建 Vue3 + Nest.js 实时通信项目:4 种方案(短轮询 / 长轮询 / SSE/WebSocket)
前端·后端
LaoZhangAI2 小时前
Google Gemini Nano与Banana AI完整部署指南:2025年轻量级AI解决方案
前端·后端
Giant1002 小时前
0 基础也能懂的 Flex 布局教程:3 步搞定网页排版
css
用户11481867894842 小时前
基于 Webpack Module Federation 的 Vue 微前端实践
前端