精讀《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 個類別:
- Base 基礎樣式
- Layout 布局樣式
- Module 模塊樣式
- State 狀態樣式
- Theme 主題樣式
我們通過這 5 種類別來拼湊出完整的 class,我感覺就是對 OOCSS 的進一步規範和約束。
命名規則
對這 5 種類別,在命名時要加上對應前綴,分別是:
- Base 屬於基礎元素,比如 div p,不需要命名
- Layout 使用 .l- 或 .layout-前綴
- Module 使用模塊名命名,比如文章區塊就叫 .article
- State 使用 .is- 前綴,比如 .is-show
- 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:前端开发 |