CSS in JS 應用與優缺點分析 實踐 styled-components 與 StyleX 模組化方案

me
林彥成
2022-09-10 | 4 min.
文章目錄
  1. 1. 什麼是 CSS in JS?
  2. 2. 什麼是 CSS in JS:解決 CSS 命名問題的新思路
    1. 2.1. 問題定義
    2. 2.2. CSS in JS 的優點與解決的問題
    3. 2.3. CSS in JS 的缺點與挑戰
  3. 3. styled-components:使用 JavaScript 撰寫 CSS 的框架
    1. 3.1. 套用主視覺配置
    2. 3.2. RWD 實作
  4. 4. styled-jsx:Next.js 內建的 CSS in JS 解決方案
  5. 5. CSS Modules:解決 CSS 命名衝突的方案
  6. 6. StyleX:Facebook 推出的 Atomic CSS in JS 解決方案
  7. 7. FAQ:CSS in JS 常見問題
    1. 7.1. Q1:CSS in JS 會導致頁面效能下降嗎?
    2. 7.2. Q2:使用 CSS in JS 該如何處理 Server-Side Rendering (SSR)?
    3. 7.3. Q3:CSS Modules 與 Styled Components 該如何選?

什麼是 CSS in JS?

CSS in JS 是一種將 CSS 樣式直接撰寫在 JavaScript 或 TypeScript 檔案中的開發模式,特別盛行於 React 生態系。其核心目標在於透過「元件化」來解決傳統 CSS 的全域命名衝突、樣式冗餘與死程式碼清理(Dead Code Elimination)等問題。主流工具如 styled-components 利用標籤模板字串實現樣式與邏輯的緊密綁定;而 CSS Modules 則透過工具自動混淆 Class 名稱達成隔離。這項技術不僅提升了開發速度與程式碼搬移的便利性,更讓樣式能根據元件的 Props 動態調整,是現代前端實現高度模組化的關鍵技術。


什麼是 CSS in JS:解決 CSS 命名問題的新思路

CSS in JS 解決了 CSS 命名 的問題。

由於 component-based 的概念興起,元件開發成為顯學,CSS in JS 提供了將樣式寫在元件中解決方案,也讓 CSS 需要從寫程式語言的角度去進行架構設計。

CSS in JS 不僅降低了維護難度也加速了開發速度,減少檔案切換和減少 class 命名錯誤等等問題,常見的 library 像是 CSS Modules、vanilla-extract、styled-components、styled-jsx (Next.js) 都非常好上手,更完整的將元件模組化並增加可重用性。

CSS in JS 缺點也要先說在前面,當需要 Server Render 時,相關的效能問題也需要一併考慮進去。

CSS in JS 像是把需要打扮的道具跟裝備直接跟使用的情境整合在一起,像是約會必勝穿搭、適合上班的妝容等等,在遇到相關情境時能夠輕鬆應對。

問題定義

  • 當元件在其他專案使用時,搬動除了必須搬完相對應的樣式檔,還必須放在正確的位置。
  • 在專案最佳化時,樣式檔的引用順序須讓機器不會混淆,若將樣式檔拆太細,很可能發生 Code Splitting 時順序錯誤。

CSS in JS 的優點與解決的問題

  • 解決 Code Splitting 可能出現的順序問題。
  • 解決命名互相覆蓋,在撰寫樣式檔時也可以用 BEM 的命名規則避開或導入原子化樣式設計
  • 維護時不需要再去找到底被哪個樣式檔影響。

CSS in JS 的缺點與挑戰

  • RWD 不像 Bootstrap 有格線系統那樣使用方便,所以可以額外導入 CSS 框架 的格線系統混合使用。
  • 較難做效果跟做覆蓋,不同狀態需要不同樣式時要再封裝一層。
  • 語法的高亮、自動完成、防呆較不完整。
  • 少了可以共用樣式的好處,像是 @extend@mixin 這樣的寫法,需把元件切的夠小,才會有一定程度的共用性。

styled-components:使用 JavaScript 撰寫 CSS 的框架

styled-components 提供了在 JavaScript 中直接撰寫 CSS 的介面,意謂著可以使用所有的 CSS 功能。切換主視覺要多用一個 <ThemeProvider>,傳入的 Props 差異太大時有點麻煩。

1
2
3
4
5
6
7
8
9
10
import styled from "styled-components";

function Sample() {
const StyledButton = styled.button`
color: #ffffff;
font-size: 20px;
border-radius: 5px;
`;
return <StyledButton type="button">按鈕</StyledButton>;
}

套用主視覺配置

styled-components 也提供了主視覺的配置,透過提供的 ThemeProvider 在外層注入就可以在元件中直接取值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import { ThemeProvider } from "styled-components";

const theme = {
colors: {
primary: "#c18e00",
secondary: "#0086ff",
},
fontSizes: {
xl: "1.875rem",
base: "1rem",
}
};

class MyApp extends App {
render() {
const { Component, pageProps } = this.props;
return (
<ThemeProvider theme={theme}>
<Component {...pageProps} />
</ThemeProvider>
);
}
}

在元件中直接取值,甚至可以寫 Function 進行判斷:

1
2
3
4
const StyleWrapper = styled.div`
background: ${({ color, reverse, theme: { colors } }) =>
reverse ? colors[color] : "none"};
`;

RWD 實作

由於 styled-components 提供了 function 的撰寫模式,所以即使撰寫 RWD 的元件也很簡單:

1
2
3
4
5
6
7
8
9
10
11
12
13
function getWidthString(span) {
if (!span) return;
return `width: ${(span / 12) * 100}%`;
}

const Column = styled.div`
float: left;
${({ xs }) => (xs ? getWidthString(xs) : "width: 100%")};

@media only screen and (min-width: 768px) {
${({ sm }) => sm && getWidthString(sm)};
}
`;

styled-jsx:Next.js 內建的 CSS in JS 解決方案

styled-jsx 則是 Next.js 內建支援的介面,寫法上稍微不同,使用 <style jsx> 標籤:

1
2
3
4
5
6
7
8
9
10
11
12
13
function Sample() {
return (
<>
<style jsx>{`
button {
color: #ffffff;
font-size: 20px;
}
`}</style>
<button type="button">按鈕</button>
</>
);
}

CSS Modules:解決 CSS 命名衝突的方案

CSS Modules 是用來解決 CSS 命名衝突 的問題。在 Create React App 中,CSS 命名規則為 {檔名}.module.css

main.module.css

1
2
3
.information {
color: red;
}

page.module.css

1
2
3
.information {
color: green;
}
1
2
3
4
5
6
7
8
9
10
11
import styleMain from "./main.module.css";
import stylePage from "./page.module.css";

export default function Test() {
return (
<div>
<h1 className={styleMain.information}>App</h1>
<h3 className={stylePage.information}>Test</h3>
</div>
);
}

StyleX:Facebook 推出的 Atomic CSS in JS 解決方案

Facebook 推出的 Atomic CSS in JS 解決方案 StyleX 上線啦!透過 Build Time 就處理好的樣式效能海放 Run Time 的 styled-components,同時也減少了要背一堆框架內建的 Util CSS 語法。

1
2
3
4
const styles = stylex.create({
red: { color: "red" },
});
let a = stylex.props(styles.red);

經過編譯後,會自動產生一個動態的 class 以及對應的樣式,完整的解決了命名問題並同時提供原子化樣式的優點。


FAQ:CSS in JS 常見問題

Q1:CSS in JS 會導致頁面效能下降嗎?

A:這取決於您使用的庫。Run-time 類型的庫(如 styled-components)會在執行期計算樣式並注入 DOM,在大型元件樹中可能會有些微開銷。而 Compile-time 類型的庫(如 StyleXVanilla Extract)則在打包時就生成靜態 CSS,效能幾乎與傳統 CSS 一致。

Q2:使用 CSS in JS 該如何處理 Server-Side Rendering (SSR)?

A:庫如 styled-components 需要額外配置(如使用 ServerStyleSheet)來收集首屏所需的樣式,並在伺服器端將其注入到 HTML 中,以避免出現無樣式內容閃爍 (FOUC) 的問題。Next.js 內建的 styled-jsx 則已處理好此部分。

Q3:CSS Modules 與 Styled Components 該如何選?

A:如果您偏好將 CSS 與 JS 完全分離,並使用傳統的 CSS 語法,CSS Modules 是穩健的選擇;如果您希望樣式與邏輯深度整合,並享受 Props 傳遞帶來的動態樣式便利性,styled-components 則能提供更優雅的開發體驗。


掌握了 CSS in JS,您就能為您的 React 元件穿上最合適的「裝備」。無論是選擇靈活的 styled-components,還是效能卓越的 StyleX,建立一致的開發規範才是專案成功的核心!


喜歡這篇文章,請幫忙拍拍手喔 🤣