《为何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.

发表评论

人生梦想 - 关注前沿的计算机技术 acejoy.com 🐾 步子哥の博客 🐾 背多分论坛 🐾 知差(chai)网 🐾 DeepracticeX 社区 🐾 老薛主机 🐾