CSS in JS (JSS)

2023-03-27 18:00:21

JSS 是什麼

簡單來說,一句話概括CSS in JS (JSS),就是"行內樣式"(inline style)和"行內指令碼"(inline script)。

因為,自從React出現以後,基於元件化的要求,強制把HTML、CSS、JavaScript捆綁在一起,在同一個檔案裡面,封裝了結構、樣式、以及邏輯。這雖然違背html發明初期的"關注點分離"的原則,但更有利於元件之間的隔離。而每個元件包含了所有需要用到的程式碼,不必依賴外部環境,元件之間沒有耦合。所以,隨著 React 的走紅和元件模式深入人心,「關注點分離」原則越發淡出人們的視野,而React所帶來的"關注點混合"的原則逐漸成為主流。[1]

React 對 CSS 封裝非常簡單,就是沿用了 DOM 的 style 屬性物件。CSS-in-JS是一種技術(technique),而不是一個具體的庫實現(library)。簡單來說CSS-in-JS就是將應用的CSS樣式寫在JavaScript檔案裡面,而不是獨立為一些.css,.scss或者less之類的檔案,這樣你就可以在CSS中使用一些屬於JS的諸如模組宣告,變數定義,函數呼叫和條件判斷等語言特性來提供靈活的可延伸的樣式定義。值得一提的是,雖然CSS-in-JS不是一種很新的技術,可是它在國內普及度好像並不是很高,它當初的出現是因為一些component-based的Web框架(例如React,Vue和Angular)的逐漸流行,使得開發者也想將元件的CSS樣式也一塊封裝到元件中去以解決原生CSS寫法的一系列問題。還有就是CSS-in-JS在React社群的熱度是最高的,這是因為React本身不會管使用者怎麼去為元件定義樣式的問題,而Vue和Angular都有屬於框架自己的一套定義樣式的方案。[2]

JSS 的常見實現

由於React 對 CSS 的封裝非常弱,導致出現了一系列的第三方庫,用來加強 React 的 CSS 操作。它們統稱為 CSS in JS,意思就是使用 JS 語言寫 CSS。根據不完全統計,各種 CSS in JS 的庫至少有47種。老實說,現在也看不出來,哪一個庫會變成主流。[1]

1. Styled-components

缺點:

  • 必須使用Styled-components預定義的語法糖,如styled.div("...")
  • 語法糖對css的封裝居然使用的是string,而使用string也就意味著我們將會失去一切可能的物件化操作css的機會。這與差不多10年前 AngularJS 1.x 時代對 html 的處理方法如出一轍,不得不說這種方式似乎是在開歷史的倒車。

不過,Styled-components 應該是CSS-in-JS最熱門的一個庫了,到目前為止github的star數已經超過了30k了。通過styled-components,你可以使用ES6的標籤模板字串語法(Tagged Templates)為需要styled的Component定義一系列CSS屬性,當該元件的JS程式碼被解析執行的時候,styled-components會動態生成一個CSS選擇器,並把對應的CSS樣式通過style標籤的形式插入到head標籤裡面。動態生成的CSS選擇器會有一小段雜湊值來保證全域性唯一性來避免樣式發生衝突。[2]

它既具備了 css-in-js 的模組化與引數化優點,又完全使用CSS的書寫習慣,不會引起額外的學習成本[3]。

Styled-components官網

2. CSS module

import style from './index.css'
<div className={style.app}>

需要額外設定,ts環境需要設定*.d.ts的型別宣告檔案

declare module "*.css" {
    const css: {
        [key: string]: string //約定:匯出key所在的物件,原始的類名和內容都會和轉化為這個物件
    };
    export default css;
}

JSS 的好處

p.s. 接下來內容均轉自 知乎/進擊的大蔥/CSS in JS的好與壞

1. 區域性樣式 - Scoping Styles

CSS有一個被大家詬病的問題就是沒有本地作用域,所有宣告的樣式都是全域性的(global styles)。而CSS-in-JS會提供自動區域性CSS作用域的功能,你為元件定義的樣式會被限制在這個元件,而不會對其他元件的樣式產生影響。[2]

2. 避免無用的CSS樣式堆積

進行過大型Web專案開發的同學應該都有經歷過這個情景:在開發新的功能或者進行程式碼重構的時候,由於HTML程式碼和CSS樣式之間沒有顯式的一一對應關係,我們很難辨認出專案中哪些CSS樣式程式碼是有用的哪些是無用的,這就導致了我們不敢輕易刪除程式碼中可能是無用的樣式。這樣隨著時間的推移,專案中的CSS樣式只會增加而不會減少(append-only stylesheets)。

而因為CSS-in-JS會把樣式和元件繫結在一起,當這個元件要被刪除掉的時候,直接把這些程式碼刪除掉就好了,不用擔心刪掉的樣式程式碼會對專案的其他元件樣式產生影響。而且由於CSS是寫在JavaScript裡面的,我們還可以利用JS顯式的變數定義,模組參照等語言特性來追蹤樣式的使用情況,這大大方便了我們對樣式程式碼的更改或者重構。[2]

3 Critical CSS

放在head標籤內的CSS當然是越少越好,因為太多的內容會加大html的體積,所以我們一般把使用者需要在首屏看到的(above the fold)頁面要用到的最少CSS提取為Critical CSS。

CSS-in-JS通過增加一點載入的JS體積就可以避免另外發一次請求來獲取其它的CSS檔案。而且一些CSS-in-JS的實現(例如styled-components)對Critical CSS是自動支援的。[2]

4. 基於狀態的樣式定義

CSS-in-JS可以根據元件的狀態動態地生成樣式。[2]

5. 封裝得更好的元件庫

如果CSS是寫在JS裡面的,專案想要使用封裝的元件庫只需要進行簡單的npm install就可以了,非常方便。[2]

JSS 的壞處

p.s. 接下來內容均轉自 知乎/進擊的大蔥/CSS in JS的好與壞

1. 陡峭的學習曲線

首先CSS-in-JS是針對component-based的框架的,這就意味著要學習CSS-in-JS你必須得學習:component-based框架(例如React),JavaScript和CSS這三樣技能。其次,即使你已經會用React,JavaScript和CSS來構建SPA應用,你還要學習某個CSS-in-JS實現(例如styled-components),以及學習一種全新的基於元件定義樣式的思考問題方式。[2]

2. 執行時消耗

由於大多數的CSS-in-JS的庫都是在動態生成CSS的, 這就意味著會有一定的效能代價[2]

3. 程式碼可讀性差

大多數CSS-in-JS實現會通過生成唯一的CSS選擇器來達到CSS區域性作用域的效果。這些自動生成的選擇器會大大降低程式碼的可讀性,給開發人員debug造成一定的影響。[2]

4. 沒有統一的業界標準

CSS-in-JS只是一種技術思路而沒有一個社群統一遵循的標準和規範,所以不同實現的語法和功能可能有很大的差異。[2]

總結

CSS-in-JS有好處也有壞處,要不要使用完全取決於同學們自己的專案需求。例如在下面幾種情況下你就不需要它:

  1. 前端開發的初學者: 由於CSS-in-JS的學習坡度很陡,剛開始學習Web開發的同學沒必要學習,可能會有挫敗感。
  2. 功能簡單的靜態頁面:邏輯互動不復雜的網站沒有必要使用CSS-in-JS。
  3. 注重樣式可讀性以及偵錯體驗: CSS-in-JS動態生成的選擇器很影響程式碼的可讀性,可能會降低你的偵錯效率。

在我們的課程中,我們CSS-in-JS和普通的css我們都會使用,所以請同學們放心,進過本課程的學習,同學們基本上能掌握如何使用不同的方式來定義React的樣式了。

參考文獻