css規範

[譯] 什麼是模塊化 CSS?

模塊化 CSS 是一組編寫代碼的原則,基於這個原則編寫的代碼具備高性能和可維護性。它起源於雅虎和 Yandex 的開發人員,目的是迎接維護大型代碼庫帶來的挑戰。有些規則在提出之初稍有爭議,但後來被認爲是最佳實踐。css

目錄:html

  1. 大規模 CSS 的處理難點
  2. 什麼是模塊化
  3. 模塊化框架
    1. OOCSS
    2. BEM
    3. SMACSS
  4. 共享模塊化原則
  5. FAQ
  6. 總結,模塊化 CSS 太美妙啦

(偷偷告訴你:若是你對這篇文章的篇幅感到不知所措,觀看視頻可能更適合你,這篇文章來源於此演講。)前端

大規模 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 以前的工做方式。設計師給我這樣的草圖:

Illustration of a design comp for a bookstore website

圖: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 系統可能包含的規則定義的類別:

  1. 基礎(Base) 規則是HTML元素的默認樣式,如連接,段落和標題。
  2. 佈局(Layout) 規則將頁面分紅幾個部分,並將一個或多個模塊組合在一塊兒。它們只定義佈局,而無論顏色或排版。
  3. 模塊(Module)(又名「對象」或「塊」)是可重用的,設計中的一個模塊。例如,按鈕,媒體對象,產品列表等。
  4. 狀態(State) 規則描述了模塊或佈局在特定狀態下的外觀。一般使用 JavaScript 應用或刪除。例如,隱藏,擴展,激活等。
  5. 主題(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 元素的默認樣式,如:ali 和 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

相關文章
相關標籤/搜索