- 原文地址:What is Modular CSS?
- 原文做者:Scott Vandehey
- 譯文出自:掘金翻譯計劃
- 本文永久連接:github.com/xitu/gold-m…
- 譯者:ssshooter
- 校對者:Hopsken Park-ma
模塊化 CSS 是一組編寫代碼的原則,基於這個原則編寫的代碼具備高性能和可維護性。它起源於雅虎和 Yandex 的開發人員,目的是迎接維護大型代碼庫帶來的挑戰。有些規則在提出之初稍有爭議,但後來被認爲是最佳實踐。css
目錄:html
(偷偷告訴你:若是你對這篇文章的篇幅感到不知所措,觀看視頻可能更適合你,這篇文章來源於此演講。)前端
大規模 CSS 的處理難點
模塊化 CSS 使用的主要場景是棘手的大規模 CSS。正如 Nicholas Gallagher 所說的:git
這句話直指大規模 CSS 問題的核心。寫代碼並不難,難的是在不讓你的代碼隨着時間的推移成爲拖累你的「技術債」。github
難以理解
如下是 CSS Guidelines 中的一個示例,這個示例展現了一個問題:除了寫這段代碼的人,沒有人知道這段代碼是幹什麼的。web
<div class="box profile pro-user"> <img class="avatar image" /> <p class="bio">...</p> </div> 複製代碼
box
和profile
有什麼關係?profile
和avatar
有什麼關係?或者他們之間真的有關係嗎?你應該在bio
旁邊添加pro-user
嗎?image
和profile
寫在同一部分 CSS 嗎?能夠在其餘地方使用avatar
嗎?編程
光看代碼沒法回答這些問題,你必須在 CSS 代碼中推理他們的做用。安全
難以複用
複用代碼會很是棘手。假設你要在另外一個頁面上覆用某個頁面上的樣式,但你想這麼作的時候,會發現那個樣式是專爲第一個頁面而寫的。代碼的做者認爲它只用在某個特定元素中,或者它是從頁面繼承某些類,在其餘環境中根本不起做用。你不想修改原來的內容,而後直接複製了代碼。bash
如今你有兩個問題:一份原始代碼,一份重複代碼,你的維護負擔直接增長了一倍。網絡
難以維護
大規模的 CSS 也難以維護。你改變了一個標籤,樣式就會像紙牌屋同樣崩潰。你想更新一個頁面上的樣式,卻破壞了另外一個頁面的樣式。你試圖覆蓋其餘頁面,但又深陷於優先度問題。
什麼是模塊化
那麼咱們如何解決這些問題呢?答案在於模塊化這個概念,但這是什麼呢?咱們先看看 Harry Roberts 對關注點分離的看法:
這是一個常見編程習慣,可是許多 CSS 開發者不太熟悉。這個思想是確保你所寫的東西不會比你想要作的更多。
舉個例子,說明我在學習模塊化 CSS 以前的工做方式。設計師給我這樣的草圖:

圖:Yandex
我會以爲:「好吧,這是一個書店頁面,側邊欄中有一些小部件,右側列出了大概是書籍封面的清單,一個精選書評,下面還有其餘的評論。」
我當時認爲一個頁面是一個完整的單元,頁面裏的較小部分從屬於頁面。這是一種自上而下的思考方法,這致使大量只服務於單個頁面的一次性代碼,不利於編寫可複用代碼。
模塊化 CSS 須要你換一個角度看問題,不從頁面級別考慮,而是關注組成頁面的小塊。這不是一個頁面而是一個組件的集合。
你會發現頁面裏包含的是 logo,搜索欄,導航,照片列表,輔助導航,標籤框,視頻播放器等。這些是能夠網站的任何位置均可以獨立使用的內容。它們只是碰巧在這個特定頁面以這種方式組合。
模塊化 CSS 是自下而上的思惟,須要從構建整個站點的可複用構建模塊開始。
這會讓你想起樂高?應該的!幾乎全部撰寫有關模塊化 CSS 的人都使用樂高進行類比。使用標準化,易於理解,而且不依賴上下文的塊來構建 UI 的是一個很好的思路。
這樣的「塊」最著名的例子之一是由 Nicole Sullivan 定義的「媒體對象」,她認爲這種對象是你將在任何網站上找到的最小的組件之一。
它將固定寬度的圖像組合到靈活寬度的容器的一側,如今處處均可以看到這個模式。她撰寫了一篇名爲 The Media Object Saves Hundreds of Lines of Code 的案例研究,談到將此模式應用於大型網站,最大的例子之一即是 Facebook:
這裏高亮顯示了 Facebook 流中的全部媒體對象。左上角我的信息,右側導航元素,訂閱的每一個帖子,甚至是廣告都是媒體對象。有時它們彼此嵌套。雖然使用目的不一樣,但它們都共享相同的基礎模式:固定寬度的圖像,彈性寬度的文本。
她的觀點是,以 Facebook 的規模運營時,媒體對象就不止幾十個,這樣的頁面上有數百上千個。所以,能夠想象若是爲複用樣式做優化,能夠節省大量代碼,這能夠帶來真正的高性能和低成本。
模塊化框架
那麼,既然咱們已經明確了模塊化的概念,那麼讓咱們看看這些年來推崇這一律唸的三大框架:
OOCSS
面向對象的 CSS(Object-Oriented CSS)/ OOCSS 是模塊化 CSS 的起源,由 Nicole Sullivan於 2009 年提出,這基於她在雅虎的工做。這個框架的核心思想是 —— 對象是可重用的模式(pattern),其視覺外觀不禁上下文決定。
- 有人質疑雅虎的能力,雅虎的前端團隊當時研發的 YUI library 是很是前沿的技術。在 2009 年,雅虎不是一家沒有前途的科技公司。
正如她在 2009 年所定義的那樣,這就是模塊化 CSS 的起源。除此以外,OOCSS 可歸結爲幾個核心原則:
上下文無關
首先,不管你把它放在哪裏,一個對象都應該看起來無差異,不該根據對象的上下文設置對象的樣式。
例如,不是將側邊欄中的全部按鈕都設置爲橙色,將主區域中的全部按鈕設置爲藍色,而是應該建立一個藍色的按鈕類,以及一個橙色的 modifier。這樣作橙色按鈕能夠在任何地方使用,它們沒有被綁定在側邊欄上,它們只是你的按鈕樣式之一。
皮膚(主題)
她談到的另外一個概念,是如何從正在應用的皮膚中抽象出對象的結構。
咱們能夠回到媒體對象的例子。它的樣式與標籤結構無關。有一個容器,一個固定寬度的圖像和內容。你能夠應用不一樣的樣式,可是不管樣式如何改變,標籤結構都是同樣的。
她建議的其餘方法是爲常見的視覺模式建立可複用的類。她給出了一個例子,在 2009 年的亞馬遜網站幾乎全部的東西都有陰影,但由於它們由不一樣設計師創做,因此類似卻不相同。經過標準化這些元素陰影,能夠優化代碼並使網站更高效。
使用 Class
當時她提出了一個很是具備爭議性的規則,但後來被廣爲接受:使用 class 來命名對象及其子元素,這樣能夠在不影響樣式的狀況下修改 HTML 標籤。
她不但願 CSS 由 HTML 標籤來肯定,這樣的話若是將標題從「h1」更改成「h4」,則沒必要更新 CSS。不管選擇哪一個標籤,該標題應該有一個固有的 class。例如,你的導航應該相似於 .site-nav
而不是 #header ul
。
不使用 ID
既然建議「老是使用 class」,那麼天然禁止使用 ID 選擇器。這與當時使用 ID 做爲命名空間的常見實踐相違背,直接引用嵌套在其中的元素。
ID 會擾亂 CSS 優先度,這是其次,對象必須是可複用的。根據定義,ID 是惟一的。所以,若是在對象上設置 ID,則沒法在同一頁面上重複使用它,缺乏了模塊化對象的要點。
BEM
接下來介紹下一個弘揚模塊化 CSS 精神的框架。BEM,三個字母分別表明 Block、Element、Modifier,BEM 也是在 2009 年提出,起源於 Yandex(能夠說是俄語版的 Google),除搜索業務外還運營網絡郵件程序,所以在編程上他們也須要解決與雅虎相同規模的難題。
他們提出了一套很是相似的代碼原則。他們的核心概念是 —— 塊(block)(Nicole 稱之爲「物體(object)」)由子元素(element)構成,而且能夠修改(modified)(或「主題化」)。
BEM 由 3 部分組成:
塊(Block)
塊是網頁邏輯和功能的獨立組件。BEM 的發起人對其提出了更詳盡的定義:
首先,塊是可嵌套的。它們應該能被包含在另外一個塊中,而不會破壞任何樣式。例如,可能在側欄中有一個標籤界面小部件的塊,該塊可能包含按鈕,這些按鈕也是一種單獨的塊。按鈕的樣式和選項卡式元素的樣式不會相互影響,一個嵌套在另外一箇中,僅此而已。
其次,塊是可重複的。界面應該可以包含同一塊的多個實例。就像 Nicole 所說的媒體對象同樣,複用塊能夠節省大量代碼。
元素(Element)
元素是塊的組成部分,它不能在塊以外使用。一個不錯的例子:一個導航菜單,它包含的項目在菜單的上下文以外沒有意義。你不會爲菜單項定義塊,菜單自己應定義爲塊,而菜單項是其子元素。
修飾符(Modifier)
修飾符定義塊的外觀和行爲。例如,菜單塊的外觀的垂直或水平,取決於所使用的修飾符。
命名約定
BEM 所作的另外一件事是定義了很是嚴格的命名約定:
.block-name__element--modifier
這看起來有點複雜,我來分解一下:
- 名稱以小寫字母書寫
- 名稱中的單詞用連字符(
-
)分隔 - 元素由雙下劃線(
__
)分隔 - 修飾符由雙連字符(
--
)分隔
你可使用一樣的方式進行更誇張的修改。經過使用 .minifig--batman
修飾符,咱們只用一個類就改變了 minifig 的每一個部分的外觀。
這是實踐中的 BEM 語法例子:
<button class="btn btn--big btn--orange"> <span class="btn__price">$9.99</span> <span class="btn__text">Subscribe</span> </button> 複製代碼
即便不看樣式代碼,你也能夠一眼就看出這段代碼會建立一個既大又橙的價格按鈕。不管你是否喜歡帶有連字符和下劃線的這種風格,擁有嚴格的命名約定是模塊化 CSS 向前邁出的一大步,這讓代碼帶有自文檔的效果!
不嵌套 CSS
就像 OOCSS 建議使用 class 而不使用 ID 同樣,BEM 也爲代碼風格做了一些限制。最值得注意的是,他們認爲不該該嵌套 CSS 選擇器。嵌套選擇器擾亂了優先度,使得重用代碼變得更加困難。例如,只需使用 .btn__price
而不是 .btn .btn__price
。
注意:這裏的嵌套指實踐中在 Sass 或 Less 嵌套選擇器的作法,但即便你沒有使用預處理器也適用,由於這關乎選擇器優先度問題。
這個原則不出問題是由於嚴格的命名約定。咱們曾經使用嵌套選擇器將它們隔離在命名空間的上下文中。而 BEM 的命名約定自己就提供了命名空間,所以咱們再也不須要嵌套。即便 CSS 的根級別的全部內容都是單個類,但這些名稱的具體程度足以免衝突。
通常來講,選擇器能夠在沒有嵌套的狀況下生效,就不要嵌套它。 BEM 容許此規則的惟一例外是基於塊狀態或其修飾符的樣式元素。例如,可使用 .btn__text
而後用 .btn--orange .btn__text
來覆蓋應用了修飾符按鈕的文本顏色。
SMACSS
咱們最後要討論的框架是 SMACSS,含義是 CSS 的可擴展性和模塊化架構(Scalable & Modular Architecture)。Jonathan Snook 於 2011 年提出了 SMACSS,當時他在雅虎工做,爲 Yahoo Mail 編寫 CSS。
他在 OOCSS 和 BEM 的基礎上添加的關鍵概念是,不一樣類別的組件須要以不一樣的方式處理。
類別(Categories)
如下是他爲 CSS 系統可能包含的規則定義的類別:
- 基礎(Base) 規則是HTML元素的默認樣式,如連接,段落和標題。
- 佈局(Layout) 規則將頁面分紅幾個部分,並將一個或多個模塊組合在一塊兒。它們只定義佈局,而無論顏色或排版。
- 模塊(Module)(又名「對象」或「塊」)是可重用的,設計中的一個模塊。例如,按鈕,媒體對象,產品列表等。
- 狀態(State) 規則描述了模塊或佈局在特定狀態下的外觀。一般使用 JavaScript 應用或刪除。例如,隱藏,擴展,激活等。
- 主題(Theme) 規則描述了模塊或佈局在主題應用時的外觀,例如,在 Yahoo Mail 中,可使用用戶主題,這會影響頁面上的每一個模塊。(這很是適用於像雅虎這樣的應用程序,但大多數網站都不會使用此類別。)
命名約定前綴
下一個原則是使用前綴來區分類別,他喜歡 BEM 明確的命名約定,但他還但願可以一目瞭然地看出模塊的類型。
l-
用做佈局規則的前綴:l-inline
m-
用做模塊規則的前綴:m-callout
is-
用做狀態規則的前綴:is-collapsed
(基礎規則沒有前綴,由於它們直接應用於 HTML 元素而不使用類。)
共享模塊化原則
這些框架的相同之處遠勝於其不一樣之處。我看到從 OOCSS 到 BEM 再到 SMACSS 的明確發展。它們的發展表明了咱們行業在性能和大規模 CSS 領域不斷增加的經驗。
你沒必要選擇其中一個框架,相反,咱們能夠嘗試定義模塊化 CSS 的通用規則。讓咱們看看這些框架共用和保留的最佳部分。
模塊化元素
模塊化系統由如下元素組成:
- 模塊(Module):(又名對象,塊或組件)一種可複用且自成一體的模式。如媒體對象,導航和頁眉。
- 子元素(Child Element): 一個不能獨立存在的小塊,屬於模塊的一部分。如媒體對象中的圖像,導航選項卡和頁眉 logo。
- 模塊修改器(Module Modifier):(又名皮膚或主題)改變模塊的視覺外觀。如左/右對齊的媒體對象,垂直/水平導航。
模塊化類別
模塊化系統中的樣式能夠分爲如下幾類:
- 基礎(Base) 規則是 HTML 元素的默認樣式,如:
a
、li
和h1
- 佈局(Layout) 規則控制模塊的佈局方式,但不控制視覺外觀,如:
.l-centered
、.l-grid
和.l-fixed-top
- 模塊(Modules) 是可複用的,獨立的 UI 組件視覺樣式,如:
.m-profile
、.m-card
和.m-modal
- 狀態(State) 規則由 JavaScript 添加,如:
.is-hidden
、.is-collapsed
和.is-active
- 助手(Helper)(又名功能)規則適用範圍小,獨立於模塊,如:
.h-uppercase
、.h-nowrap
和.h-muted
模塊化規則
在模塊化系統中編寫樣式時,請遵循如下規則:
- 不要使用 ID
- CSS 嵌套不要超過一層
- 爲子元素添加類名
- 遵循命名約定
- 爲類名添加前綴
FAQ
這麼作 HTML 不會有不少類嗎?
模塊化 CSS 最多見的反對意見就是,它會在 HTML 中產生許多類。我認爲這是由於長期以來 CSS 的最佳實踐都認爲應該避免大量 class 使用。早在 2011 年,Nicole Sullivan 就寫了一篇很棒的博文 Our (CSS) Best Practices are Killing Us,明確駁斥了這個想法。
我看到一些開發人員提倡使用預處理器的 extend
函數將多個樣式鏈接成一個類名。我建議不要這樣作,由於它會使你的代碼不那麼靈活。他們不能讓其餘開發者以新的方式組合你的樂高積木,而是固定了你定義的幾種組合。
BEM 的類名又長又醜!
不要由於類名太長而懼怕,他們是自文檔的!當我看到 BEM 風格的類名(或任何其餘模塊化命名約定)時,我會以爲很愉悅,由於只要看一眼就能知道這些類的含義。你能夠在 HTML 中清晰理解它們。
孫元素的命名如何約定?
長話短說:沒這回事。
模塊化 CSS 初學者能夠快速掌握子元素的概念:minifig__arm
是 minifig
的一部分。然而,有時候他們處理 CSS 中的 DOM 結構時,會疑問如何做深層嵌套,好比 minifig__arm__hand
。
沒有必要這樣作。請記住,這個思路是要將樣式與標記分離。不管 hand
是 minifig
的直接子元素仍是嵌套了多少層,都可有可無。CSS 關心的只有 hand
是 minifig
的孩子。
.minifig {}
.minifig__arm {}
.minifig__arm__hand {} /* don't do this */ .minifig__hand {} /* do this instead */ 複製代碼
模塊衝突怎麼辦?
模塊化 CSS 初學者比較關注的另外一件事是模塊之間的衝突。例如,若是我將 l-card
模塊和 m-author-profile
模塊同時應用於同一個元素,是否會致使問題?
答案是:理想狀況下,模塊不該該重疊太多。在這個例子中,l-card
模塊關注佈局,而 m-author-profile
模塊關注樣式,你可能會看到 l-card
設置寬度和邊距,而 m-author-profile
設置背景顏色和字體。
測試模塊是否衝突的一種方法是以隨機順序加載它們。你能夠將項目構建配置中設定爲在構建時隨機交換樣式位置。若是看到bug,就證實你的 CSS 須要以特定順序加載。
若是你發現須要將兩個模塊應用於同一個元素而且它們存在衝突,請考慮它們是否真的是兩個獨立的模塊。也許它們能夠用一個修飾符組合成一個模塊?
該規則的最後一個例外是「helper」或「utility」類可能會發生衝突,在這些狀況下,你能夠安全地考慮使用 !important
。我知道,你曾被告知 !important
不是什麼好東西,永遠不該該被使用,但咱們的作法有細微的差異:主動使用它來確保 helper 類老是優先仍是不錯的。 (Harry Roberts has more to say on this topic in the CSS Guidelines。)
總結,模塊化 CSS 太美妙啦
咱們來簡要回顧一下,還記得這段代碼嗎?
<div class="box profile pro-user"> <img class="avatar image" /> <p class="bio">...</p> </div> 複製代碼
box
和profile
有什麼關係?profile
和avatar
有什麼關係?或者他們之間有關係嗎?你應該在bio
旁邊添加pro-user
嗎?image
和profile
寫在同一部分 CSS 嗎?能夠在其餘地方使用avatar
嗎?
如今咱們知道如何解決這些問題了。經過編寫模塊化 CSS 並使用適當的命名約定,咱們能夠編寫自文檔的代碼:
<div class="l-box m-profile m-profile--is-pro-user"> <img class="m-avatar m-profile__image" /> <p class="m-profile__bio">...</p> </div> 複製代碼
咱們能夠看到哪些類彼此相關,哪些類彼此不相關,以及如何相關。咱們知道在這個組件的範圍以外咱們不能使用哪些類,固然,咱們還知道哪些類能夠在其餘地方複用。
模塊化 CSS 簡化了代碼並推動了重構,產出自文檔的代碼,這樣的代碼不影響外部做用域且可複用。
或者換句話說,模塊化 CSS 是可預測的,可維護的而且是高性能的。
轉發自掘金做者ssshooter