如何寫出一套可維護的 CSS 庫?

前言

如何寫出一套可維護的 CSS 庫?不妨談談 CSS 的設計模式 / 架構吧。接下來將爲你講述三個主流的 CSS 設計思想和一個最近通用的 CSS 設計思想:OOCSS、SMACSS、BEMCSS、METACSS。

OOCSS

OOCSS, 字面意思是面向對象的 CSS,是由 Nicole Sullivan 提出的 css 理論,雖說是理論,實則更像一種程序員間約定的規範:

在 OOCSS 的觀念中,強調重複使用 class,而應該避免使用 id 作爲 CSS 的選擇器。OOCSS 追求元件的複用,其 class 命名更爲抽象,一般不體現具體事物,而注重表現層的抽取。

SMACSS

smacss 通過一個靈活的思維過程來檢查你的設計過程和方式是否符合你的架構

設計的主要規範有三點:

Categorizing CSS Rules

這一點是 SMACSS 的核心。SMACSS 認爲 css 有 5 個類別,分別是:1 Base 2 Layout 3 Module 4 State 5 Theme or Skin

Base Rules

基礎規範, 描述的是任何場合下,頁面元素的默認外觀。它的定義不會用到 class 和 ID。css reset 也屬於此類。常見的如 normalize.css,CSS Tools

Layout Rules

佈局規範, 元素是有層次級別之分的,Layout Rules 屬於較高的一層,它可以作爲層級較低的 Module Rules 元素的容器。左右分欄、柵格系統等都屬於佈局規範。佈局是一個網站的基本,無論是左右還是居中,甚至其他什麼佈局,要實現頁面的基本瀏覽功能,佈局必不可少。SMACSS 還約定了一個前綴 l-/layout - 來標識佈局的 class。舉個最普遍的例子。

.layout-header {}
.layout-container {}
.layout-sidebar {}
.layout-content {}
.layout-footer {}
Module Rules

模塊規範, 模塊是 SMACSS 最基本的思想,同時也是大部分 CSS 理論的基本,將樣式模塊化就能達到複用和可維護的目的,但是 SMACSS 提出了更具體的模塊化方案。SMACSS 中的模塊具有自己的一個命名,隸屬於模塊下的類皆以該模塊爲前綴,例子如下:

.todolist{}
.todolist-title{}
.todolist-image{}
.todolist-article{}

可以看到 todolist 作爲一個模塊,包含了 title,image,article 等組件,同時還可以加上如. todolist-background-danger 等修飾類,在模塊內可以使用其名稱做前綴任意組織模塊結構,但目的是讓其變得更易用,提高可擴展性和靈活度,如果只是爲了修飾而修飾,寫出大量沒有任何複用性的類,便是一種弄巧成拙的做法。

State Rules

狀態規範, 這個應該很多前端開發者都很好理解,描述的是任一元素在特定狀態下的外觀。例如,一個消息框可能有 success 和 error 等狀態。與 OOCSS 抽取修飾類的方式的不同,SMACSS 是抽取更高級別的樣式類,得到更強的複用性,如隱藏某個元素的寫法:

.is-hidden{
    display: none;
}
Theme Rules

主題規範, 描述了頁面主題外觀,一般是指顏色、背景圖。Theme Rules 可以修改前面 4 個類別的樣式,且應和前面 4 個類別分離開來(便於切換,也就是 “換膚”)。SMACSS 的 Theme Rules 不要求使用單獨的 class 命名,也就是說,你可以在 Module Rules 中定義. header{ } 然後在 Theme Rules 中也用. header { }來定義需要修改的部分(後加載覆蓋前加載樣式內容)

Naming Rules

命名規範

按照前面 5 種的劃分:

Base Rules(Pass)

Layout Rules 用 l - 或 layout - 這樣的前綴,例如:.l-header、.l-sidebar。

Module Rules 用模塊本身的命名,例如圖文排列的. media、.media-image。

State Rules 用 is - 前綴,例如:.is-active、.is-hidden。

Theme Rules 如果作爲單獨 class,用 theme - 前綴,例如. theme-a-background、.theme-a-shadow。

Minimizing the Depth of Applicability

最小適配深度原則,簡單的例子:

/* depth 1 */
.sidebar ul h3 { }

/* depth 2 */
.sub-title { }

兩段 css 的區別在於 html 和 css 的耦合度 (這一點上和 OOCSS 的分離容器和內容的原則不謀而合)。可以想到,由於上面的樣式規則使用了繼承選擇符,因此對於 html 的結構實際是有一定依賴的。如果 html 發生重構,就有可能不再具有這些樣式。對應的,下面的樣式規則只有一個選擇符,因此不依賴於特定 html 結構,只要爲元素添加 class,就可以獲得對應樣式。

當然,繼承選擇符是有用的,它可以減少因相同命名引發的樣式衝突(常發生於多人協作開發)。但是,我們不應過度使用,在不造成樣式衝突的允許範圍之內,儘可能使用短的、不限定 html 結構的選擇符。這就是 SMACSS 的最小化適配深度的意義。

BEMCSS

BEM 分別代表着:Block(塊)、Element(元素 / 子塊 / 組成部分)、Modifier(修飾符),是一種組件化的 CSS 命名方法和規範,由俄羅斯 Yandex 團隊所提出。其目的是將用戶界面劃分成獨立的(模)塊,使開發更爲簡單和快速,利於團隊協作開發。

特點

組件化 / 模塊化的開發思路。書寫方式解耦化,不會造成命名空間的污染,如:.xxx ul li 寫法帶來的潛在嵌套風險。命名方式化扁平,避免樣式層級過多而導致的解析效率降低,渲染開銷變大。組件結構獨立化,減少樣式衝突,可以將已開完成的組件快速應用到新項目中。有着較好的維護性、易讀性、靈活性。規則

BEM 的命名模式在社區中有着不同方式,以下爲 Yandex 團隊所提出的命名規則爲(本文中的代碼使用該規則):

.[Block 塊]__[Element 元素]_[Modifier 修飾符] 不同的命名模式,區別在於 BEM 之間的連接符號不同,依個人而定:

.[Block 塊]__[Element 元素]--[Modifier 修飾符] 任何一種規範,都是基於實際需求而定,便於團隊開發和維護擴展,每個規範都是經過合理評估後所得出的一種 “思路” 和“建議”。

Block(塊)

是一個獨立的實體,即通常所說的模塊或組件。

例:header、menu、search

規則 塊名需能清晰的表達出,其用途、功能或意義,具有唯一性。塊名稱之間用 - 連接。每個塊名前應增加一個前綴,這前綴在 CSS 中有命名空間(如:m-、u-、分別代表:mod 模塊、ui 元件)。每個塊在邏輯上和功能上都相互獨立。由於塊是獨立的,可以在應用開發中進行復用,從而降低代碼重複並提高開發效率。塊可以放置在頁面上的任何位置,也可以互相嵌套。同類型的塊,在顯示上可能會有一定的差異,所以不要定義過多的外觀顯示樣式,主要負責結構的呈現。這樣就能確保塊在不同地方複用和嵌套時,增加其擴展性。綜上所述,最終我們可以把 BEM 規則最終定義成:

.[命名空間]-[組件名 / 塊]__[元素名 / 元素]--[修飾符] 情景 需要構建一個 search 組件。

寫法 .m-search{} 結構

如果打算開發一套框架,可以使用具有代表性的縮寫,用來表示命名空間:Element UI(el-)、Ant Design(ant-)、iView(ivu-)。

Element(元素)

是塊中的組成部分,對應塊中的子元素 / 子節點。

例:header title、menu item、list item

規則 元素名需能簡單的描述出,其結構、佈局或意義,並且在語義上與塊相關聯。塊與元素之間用__連接。不能與塊分開單獨使用。塊的內部元素,都被認爲是塊的子元素。一個塊中元素的類名必須用父級塊的名稱作爲前綴,因此不能寫成:block__elem1__elem2。情景 search 組件中包含 input 和 button,是列表中的一個子元素。

寫法 .m-search{} .m-search__input{} .m-search__button{} 結構

<!-- search 組件 -->
<form class="m-search">
    <!-- input 是 search 組件的子元素 -->
    <input class="m-search__input">
    <!-- button 是 search 組件的子元素 -->
    <button class="m-search__button">Search</button>
</form>

原則上書寫時不會出現兩層以上的嵌套,所有樣式都爲平級,嵌套只出現在 .m-block_active ,狀態激活時的情況。

Modifier(修飾符)

定義塊和元素的外觀、狀態或類型。

例:color、disabled、size

規則 修飾符需能直觀易懂表達出,其外觀、狀態或行爲。修飾符用_連接塊與元素。修飾符不能單獨使用。在必要時可進行擴展,書寫成:block__elem_modifier_modifier,第一個 modifier 表示其命名空間。情景 假定 search 組件有多種外觀,我們選擇其中一種。並且在用戶未輸入內容時,button 顯示爲禁用樣式。

寫法 .m-search{} .m-search_dark{} .m-search__input{} .m-search__button{} .m-search__button_disabled{} 結構

<!-- dark 表明 search 組件的外觀 -->
<form class="m-search m-search-form_dark">
    <input class="m-search__input">
    <!-- disabled 表明 search__button 的狀態 -->
    <button class="m-search__button m-search__button_disabled">Search</button>
</form>
小結

很多人覺得 BEM 寫法難看,審美本是 “智者見智,仁者見仁” 的事。剛剛接觸可能是會覺得有點奇怪,但所有東西都有一個適應過程。如果僅僅爲了好看,規避其優點,我認爲得不償失。個人建議可以嘗試使用 BEM 規範來書寫代碼。

BEM 命名會使得 Class 類名變長,但經過 GZIP 等壓縮後,文件的體積其實並無太大影響。

就和早年提出 CSS 語義化 一樣,不要爲了語義而去語義,語義化本身的作用就是幫助大家更好的識別代碼,所有的規範都是基於項目的發展和團隊的協作,團隊可以根據成員的意願選擇最合適的方式。

METACSS

一些寫在全局的通用方法,是 SMACSS 中通用方法思想的分支,一般以 css 屬性、Emmet css 縮寫或功能來命名,通常以一個 css 屬性爲一個單位

表示屬性的:

.df { display: flex; }

表示功能的:

.tcut {  
  text-overflow: ellipsis;
  white-space: nowrap;
  overflow: hidden;
  }

以此類推,封裝好放到全局來使用,快速添加屬性來開發頁面。

個人總結

smacss 覆蓋了所有的細節點;bemcss 着重 css 的命名和語義化;oocss 着重可複用,把每一個 dom 節點當成一個對象,是 css 返璞歸真的思想;metacss 着重快速開發快速添加屬性,顆粒度更細,通過在 html 代碼中添加類名來添加屬性,不必再去找相對應的選擇器中的 css 代碼來修改樣式。

作者: 孟酋長

https://juejin.cn/post/6958690548009926687

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/w3BKc3C9fCcms3Cb853Cdg