为何 CSS-in-JS 从宠儿变弃儿?

在前端开发的潮流舞台上,CSS-in-JS 曾如同一颗耀眼的明星,带着模块化的光芒席卷了 React 生态。然而,2022 年,当 Emotion 排名第二的维护者 Sam 所在公司宣布弃用这一技术时,仿佛一颗重磅炸弹在社区炸开涟漪。SegmentFault 上的黄子毅在文章《精读〈我们为何弃用 CSS-in-JS〉》中,将这场技术“分手”剖析得淋漓尽致。他写道:“CSS-in-JS 既有让人拍案叫绝的优点,也有让人抓狂的缺点,尤其是性能问题,像一座无法翻越的大山。”这场从热恋到分手的旅程究竟发生了什么?让我们走进这场代码界的“时尚叛逃”,用通俗的比喻和风趣的叙述,探寻 CSS-in-JS 的起落真相。


🌟 时尚新宠的诞生:CSS-in-JS 为何风靡一时

CSS-in-JS 的出现就像一位新晋设计师,为前端开发带来了一股清新风尚。它的核心理念是将 CSS 样式直接写入 JavaScript 文件,与组件绑定,彻底告别传统 CSS 的全局混乱。黄子毅在文章中提到:“它就像 JS 文件天然支持模块化一样,解决了原生 CSS 全局污染的老大难问题。”让我们走进它的“时尚秀场”,看看它为何一度成为宠儿。

🌈 模块化的天生丽质

传统 CSS 就像一个没有围栏的花园,谁都能闯进来撒点“杂草”。CSS-in-JS 则像给每个组件建了个私人小院,确保样式只在自家地盘生效。比如,用 Styled-Components 写一个按钮:

const Button = styled.button` background: teal; color: white; `;

这就像给按钮量身定制了一套西装,再也不用担心隔壁导航栏的红色跑过来捣乱。相比之下,传统 CSS 若不用 BEM 命名法,就得靠开发者小心翼翼地避免冲突。

🤝 与 JS 的无缝联姻

CSS-in-JS 让样式和逻辑住进了同一个“房间”。黄子毅指出:“它天然融入 JS,方便模块化管理。”这就像把设计师和工程师塞进一个办公室,随时沟通需求。比如,一个 React 组件的样式直接跟代码绑定,不用再翻遍文件夹找对应的 .css​文件。这种亲密关系让开发体验如丝般顺滑。

动态样式的魔法手杖

最迷人的一点是,CSS-in-JS 能轻松利用 JS 变量打造动态样式。比如:

const Button = styled.button` background: ${props => props.active ? 'blue' : 'grey'}; `;

这就像给衣服装了个调色遥控器,想变啥颜色就变啥。相比之下,CSS 变量虽也能做到,但远不如这种直观写法来得痛快。inline-style 也能实现动态,但会让代码冗余得像堆满重复布料的仓库。

这些优点让 CSS-in-JS 在 React 生态中迅速走红,尤其在追求模块化和灵活性的团队中,它就像一位炙手可热的新星。


华服下的裂痕:CSS-in-JS 的致命缺陷

然而,时尚的光环下往往藏着不为人知的瑕疵。黄子毅在精读中一针见血:“CSS-in-JS 的缺点,尤其是性能问题,让它从宠儿变成了弃儿。”让我们掀开它的华丽外衣,看看那些让人头疼的裂痕。

💥 运行时的性能炸弹

CSS-in-JS 最大的痛点在于运行时解析。黄子毅写道:“它在 React18 调度机制下会导致渲染暂停,浏览器反复解析样式,性能瓶颈无解。”比如:

function App() { return <div css={{ color: "red" }} />; }

每次组件重渲染,React 都要重新解析样式、生成 className,再插入页面。这就像厨师每次端菜前都得现做一份酱料,而不是提前备好。相比之下,传统 CSS 直接交给浏览器渲染,效率高得像流水线生产。

🎒 包体积的隐形负担

运行时解析不仅慢,还带来了额外的包体积。黄子毅提到:“CSS-in-JS 增加了 8kb 左右的框架代码。”这就像出门旅行,非得带个大行李箱,虽然不重,但总让人觉得多余。相比之下,CSS-Modules 几乎零负担,像轻装上阵的背包客。

🛠️ 开发工具的噩梦

CSS-in-JS 还会让 ReactDevTools 变得复杂。黄子毅指出:“它包裹额外的 React 组件层,导致调试时结构乱如麻。”这就像给代码穿了件层层叠叠的洋葱装,剥开一看,里面全是框架生成的“洋葱皮”。

🔍 深藏的暗坑

除了显而易见的缺点,黄子毅还挖出了三个使用后才察觉的坑:

  1. 版本冲突的定时炸弹:多个 CSS-in-JS 库共存可能导致语法不兼容,就像衣柜里塞满不同品牌的衣服,搭配起来乱七八糟。
  2. 样式优先级的黑洞:插入顺序不可控,业务只能靠 !important​硬撑,像时尚秀上模特们抢着穿最显眼的衣服。
  3. SSR 适配的噩梦:不同 React 版本需要不同实现,对框架作者来说简直是“噩梦定制”。

这些问题让 CSS-in-JS 的时尚光环逐渐褪色,尤其是性能问题,像一座无法逾越的高山,逼得 Sam 的公司选择了“分手”。


🏃‍♂️ 逃离性能深渊:为何转向 CSS-Modules

面对无解的性能困境,Sam 的公司果断转向了 CSS-Modules。黄子毅分析道:“CSS-Modules 兼顾了模块化和与 JS 的绑定,还能通过语法糖曲线实现动态样式。”这就像换了件轻便又实用的运动装,既保留了时尚感,又甩掉了沉重负担。

🌟 性能的救赎

CSS-Modules 无需运行时解析,直接生成静态样式,交给浏览器高效处理。黄子毅举例:“它避免了 React 重渲染时的样式重复计算。”这就像把菜提前做好,顾客一来就能上桌,而不是现炒现等。

🤝 模块化的延续

CSS-Modules 也能隔离样式,像 CSS-in-JS 一样避免全局冲突。比如:

/* styles.module.css */ .button { background: teal; }
import styles from './styles.module.css'; <div className={styles.button} />;

这就像给每个组件发了个独立 ID 卡,互不干扰。

动态的曲线救国

虽然 CSS-Modules 无法直接用 JS 变量,但黄子毅提到:“通过 :import​和 :export​伪类,配合 webpack-loader,可以实现 JS 与 CSS 变量的双向引用。”这就像在运动装上加了个小口袋,虽然不如遥控器方便,但也能装下必需品。

这种折中方案让 CSS-Modules 成了性能与功能的完美平衡点,难怪 Sam 的公司毫不犹豫地“换装”。


📏 包体积的真相:CSS-in-JS 其实不胖?

有趣的是,黄子毅对原文“增加 8~16kb”的说法提出了质疑:“除非你的项目只有一行 CSS,否则 CSS-in-JS 在大型项目中可能是最优的。”他解释道,CSS-in-JS 按需插入样式,未渲染的组件不会增加负担,还能合并相同样式,像 webpack 抽取公共代码一样。这就好比定制服装,只做你穿的那件,而不是批量生产一堆没人要的库存。

然而,这种优势建立在忽略运行时性能的前提下。一旦渲染频繁,包体积的优势就被性能的拖累吞噬,就像一件轻薄外套在暴风雨中毫无用处。


🔮 编译时的曙光:CSS-in-JS 的救赎之路?

为了解决运行时问题,社区推出了编译时 CSS-in-JS 方案,如 vanilla-extract。黄子毅举例:

import { style } from '@vanilla-extract/css'; const myStyle = style({ display: 'flex', paddingTop: '3px' }); const App = () => <div className={myStyle} />;

这种方案在编译时生成静态样式,避免了运行时开销。就像把衣服提前裁好,而不是现场缝制。然而,它牺牲了灵活性,无法像运行时方案那样随心所欲地写内联样式。

黄子毅一针见血:“编译时方案本质上是变种的 CSS-Modules,只是用 .ts​定义样式更贴近 JS。”这就像给运动装换了个牌子,内核还是那件老衣服。既然如此,为何不直接用更成熟的 .scss​或 .less​呢?


🌍 用户的无言:技术争论下的沉默观众

这场技术叛逃的背后,用户却像看戏的路人,始终沉默。黄子毅并未直接提及用户视角,但我们可以推测:用户只关心页面快不快、好不好用,不会在乎你是用 CSS-in-JS 还是 CSS-Modules。这就像餐厅顾客,只关心菜好不好吃,不会问厨师用的是哪把刀。

技术的选择最终服务于产品,而非开发者的喜好。CSS-in-JS 的弃用,正是因为它无法在性能上满足用户的需求。


🎭 团队的抉择:实用压倒浪漫

对于开发团队来说,技术的浪漫远不如实用重要。黄子毅总结:“CSS-in-JS 的方向是对的,但运行时的灵活性带来了无解的性能问题。”Sam 公司的选择反映了一个现实:当性能成为瓶颈,团队宁愿放弃动态的“魔法”,换取稳定的“老规矩”。

这就像时尚界的设计师,最终还是得考虑衣服能不能穿出门,而不是只追求秀场上的惊艳。


🌈 尾声:叛逃背后的启示

CSS-in-JS 的起落,就像一场时尚界的叛逃剧。从风靡一时的宠儿,到因性能缺陷被抛弃,它告诉我们:技术的价值不在新奇,而在平衡。黄子毅感慨:“编译时方案可能是出路,但若失去灵活性,还不如回归传统。”这场叛逃没有绝对的胜者,只有适合的选择。

你呢?是会怀念 CSS-in-JS 的灵动,还是拥抱 CSS-Modules 的稳重?欢迎在评论区聊聊你的故事,毕竟,代码的世界里,每一次抉择都是一场冒险。


参考文献

  1. 黄子毅. 精读《我们为何弃用 CSS-in-JS》 . https://segmentfault.com/a/1190000042798910. 2022-11-14.
  2. Sam. Why We're Breaking Up with CSS-in-JS. [原文链接已加密,参考黄子毅文章引用].
  3. Emotion Documentation. Official Documentation. https://emotion.sh/docs.
  4. Vanilla Extract Documentation. Official Documentation. https://vanilla-extract.style/.
  5. Styled-Components Documentation. Official Documentation. https://styled-components.com/docs.

  • CSS

    CSS(Cascading Style Sheet)“层叠样式表”是用于控制网页样式并允许将样式信息与网页内容分离的一种标记性语言。

    199 引用 • 542 回帖 • 1 关注

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...