標籤:

精讀《css-in-js 殺雞用牛刀》

本期精讀的文章是:css-in-js 殺雞用牛刀

1 引言

繼 精讀《請停止 css-in-js 的行為》 這篇文章之後,我們又讀了一篇抵制 css-in-js 的文章,雖然大部分觀點都有道理,但部分存在可商榷之處,讓我們分析一下這篇文章,了解 css 還做了哪些努力,以及 css-in-js 會如何發展。

2 內容概要

2.1 結構/行為 vs 樣式

作者認為,模塊化 jsx 讓 html 結構與行為耦合在一起是很有價值的,然而樣式卻不應該與模塊耦合起來,因為樣式是一種全局行為。許多時候需要對網站進行全局的設計,將樣式分散到模塊中會導致更多的理解成本。

2.2 松耦合與緊耦合

將樣式與模塊松耦合,系統會獲得更大的自由度與拓展性。如果樣式與結構松耦合,一套看似相似的的元素,可能擁有完全不同的底層結構。然而交互必須與結構緊耦合,因為交互依賴於結構。

2.3 視覺一致性問題

局部樣式會阻礙視覺一致性,只有全局化樣式才能保證視覺一致性。

2.4 代碼復用問題

如果每個組件維護自己的樣式,那麼會存在許多樣式代碼複製粘貼的問題,複製粘貼的代碼可維護性極低。

3 精讀

無論是 css-in-js 還是 css 預編譯的嘗試,各自都具有強大優點,本文對 css-in-js 提出的質疑我認為是欠妥當的,下面談談 css-in-js 如何解決作者提出的問題,以及簡單介紹 OOCSS, SMACSS, BEM, ITCSS, 和 ECSS 的思路。

3.1 css-in-js 依然具備視覺一致性

文中提出,網站樣式要從全局考慮,模塊化樣式行為的優點是解決了樣式衝突問題,但因此也削弱了對全局樣式的把控。

開發單個組件的樣式分為兩種情況,分別是明確風格的組件與樣式獨立的組件,在樣式獨立組件中,由於不確定會被哪些主題的網站所引用,因此無論是全局 css 還是局部 css,都無法控制樣式。在明確風格的情況下,可以先把此風格的基色確定下來,無論是抽成 sass 變數還是 js 變數,都具有可復用性。

全局 css 的開發,適合自上而下控制,組件通過定義 class 而不需要關心具體樣式,通過全局 class 統一調控整體風格。而 css-in-js 是自下而上的,但需要預先抽出整體風格的樣式模塊,其效果與全局 css 是等價的。

全局 css 控制風格:

<style>nt.container{}nt.list-item{}nt.submit-button{}n</style>nn<div className="container">nt<div className="list-item"></div>nt<div className="list-item"></div>nt<div className="submit-button"></div>n</div>n

css-in-js 風格:

const CommonContainer = styled.div``nconst CommonListItem = styled.div``nconst CommonSubmitButton = css``nnexport const Container = styled(CommonContainer)``nexport const ListItem = styled(CommonListItem)``nexport const CommonSubmitButton = styled.div`nt${CommonSubmitButton}n`n

而 css-in-js 運行時的樣式解析,讓我們更輕易的切換主題。比如我們抽出一個公共樣式包,業務代碼中的色值都從此樣式包中引用,那麼在不同的環境下,公共樣式包可能通過所在宿主環境的判斷,返回給業務代碼不同的色值,甚至與宿主環境配合,從宿主環境拿到注入的顏色,實現一套代碼在運行時輕鬆換膚。

3.2 css-in-js 仍具備代碼復用性

文中觀點提出,css-in-js 這種局部樣式行為,會導致公共樣式、方法難以復用,導致各個模塊參雜著大量重複代碼。因為 sass 通過定義全局變數、mixins 方法讓樣式更具有復用性。

我覺得這是一種誤解,在 css-in-js 模式中,通過全局合理的設計,使用 js 文件存放顏色變數、公共方法、可能會復用的 css 代碼塊,其復用能力遠大於 sass。

3.3 OOCSS

OOCSS 成為 css 的面向對象加強版,每個 class 只處理一件事:

.size {width: 25%;}n.bgBlue {background:blue}n.g-bd2{margin:0 0 10px;}n

網易 NEC 就大量使用了這種思想。

這樣的好處在於避免了 class 之間的冗餘,讓我們更容易創建可復用的 class,也不會在命名上糾結。

然而,先不說 oocss 帶來的巨大零散 class 導致的維護成本,以及修改 class 導致的巨大風險,class 的本意是語義化,如果讓 class 使用一堆對象描述堆砌,我們將很難定位一個元素,也很難描述這個元素的含義。

3.4 SMACSS

為css分類

SMACSS 認為 css 有 5 個類別:

  1. Base 基礎樣式
  2. Layout 布局樣式
  3. Module 模塊樣式
  4. State 狀態樣式
  5. Theme 主題樣式

我們通過這 5 種類別來拼湊出完整的 class,我感覺就是對 OOCSS 的進一步規範和約束。

命名規則

對這 5 種類別,在命名時要加上對應前綴,分別是:

  1. Base 屬於基礎元素,比如 div p,不需要命名
  2. Layout 使用 .l- 或 .layout-前綴
  3. Module 使用模塊名命名,比如文章區塊就叫 .article
  4. State 使用 .is- 前綴,比如 .is-show
  5. Theme 使用 .theme- 前綴

我覺得這樣在語義化的基礎上,拆分了狀態、主題、布局,著實增強了 css 可讀性。

最小化適配深度

儘可能減少適配層級,雖然增加適配層級會減少衝突發生率,但是會增加額外的閱讀負擔,以及一些 bug(舊版 ie 層級超過 255 導致樣式失效)。

像 css-modules 這種解決方案恰恰反其道而行之,通過層級避免衝突,通過預編譯解決閱讀負擔,然而在沒有預編譯的情況下,最小化適配深度原則依然是最有效的。

3.5 BEM

BEM 規範更像是 SMACSS 分類的加強版,通過 __element 表述後代,--modifier 表述狀態,比如:

.article {}n.article__label {} /* label 元素 */n.article__label--selected {} /* label 元素處於被選中狀態 */n

3.6 ITCSS

類似 SMACSS 對 css 元素進行了分層:

  • Settings – 與預處理器一起使用,包含顏色、字體等定義
  • Tools – 工具與方法,比如 mixins,Settings 與 Tools 都不會產生任何 css 代碼,僅僅是輔助函數與變數
  • Generic – 通用層,比如 reset html、body 的樣式
  • Elements – 對通用元素的樣式重置,比如  a p div 等元素的樣式重置
  • Objects – 類似 OOCSS 中的對象,描述一些常用的基礎狀態
  • Components – 對組件樣式的定義,一個 UI 元素基本由 Objects 與 Components 組成
  • Utilities – 工具類,比如 .hidden

ITCSS 的分層是非常有借鑒意義的,即便在 css-in-js 設計中,也可以參考此模式定義結構。

3.7 ECSS

ECSS 的規範是這樣的:.nsp-Component_ChildNode-variant

  • nsp 一個盡量簡短的命名空間
  • Component 文件名
  • ChildNode 子元素名
  • variant 額外內容

例子:

<div class="tl-MediaObject">n <a href="#" class="tl-MediaObject_Link">n <img class="tl-MediaObject_Media" src="mini.jpg" alt="User">n </a>n <div class="tl-MediaObject_Attribution">@BF 14 minutes ago</div>n</div>n

更多細節可以看此 PPT

4 總結

雖然我認為這篇文章提出的 css-in-js 缺點大部分存在漏洞,但它警示了我們,css 設計的初衷是全局化控制樣式,即便產生了樣式衝突、混亂的問題,但我們仍要記住,在模塊化開發的今天,仍要保持網站風格的整體性,即便使用了 css-in-js 的開發方式。

雖然作者呼籲我們不要只顧著 css-in-js,要放眼看看 OOCSS, SMACSS, BEM, ITCSS, 和 ECSS 等基於原生 css 的解決方案,但我覺得把這些思想運用到 css-in-js 是個不錯的選擇 :p

討論地址是:精讀《css-in-js 殺雞用牛刀》 · Issue #38 · dt-fe/weekly

如果你想參與討論,請點擊這裡,每周都有新的主題,每周五發布。

推薦閱讀:

天天演算法 | Easy | 10. 有效括弧:Valid Parentheses
說走就走的性能優化之旅
前端開發的模塊化和組件化的定義,以及兩者的關係?
參加FEDAY 2016是一種怎樣的感受?

TAG:前端开发 |