我為什麼認為 CSS-in-JS 是一個失敗的技術?

image.png

在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。

// 這種寫法,在當時看來,簡直是天才般的優雅
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來改寫:

/* button.module.css */
.button {
  background: var(--button-bg, white);
  color: var(--button-text, palevioletred);
  /* ...其他樣式 */
}
// 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 實戰中學到的寶貴經驗🙂。


原文出處:https://juejin.cn/post/7549950907413528610


精選技術文章翻譯,幫助開發者持續吸收新知。

共有 0 則留言


精選技術文章翻譯,幫助開發者持續吸收新知。
🏆 本月排行榜
🥇
站長阿川
📝10   💬6   ❤️6
453
🥈
我愛JS
📝1   💬5   ❤️4
94
🥉
AppleLily
📝1   💬4   ❤️1
55
#4
💬1  
5
#5
xxuan
💬1  
3
評分標準:發文×10 + 留言×3 + 獲讚×5 + 點讚×1 + 瀏覽數÷10
本數據每小時更新一次