項目開發(fā)過程中,一套合適的開發(fā)風(fēng)格指南能夠有效提高實際開發(fā)速度,降低維護(hù)成本。然而在有些項目開發(fā)過程中,CSS并沒有完善的結(jié)構(gòu)或者遵循命名約定,這導(dǎo)致在項目迭代的過程中,CSS結(jié)構(gòu)變得越來越冗余,既降低了開發(fā)效率也影響性能。
現(xiàn)有的CSS編寫策略有很多,如OOCSS,SMACSS,SUIT CSS等等,今天我們要介紹的其中一種——BEM。
什么是BEM
BEM是Block,Element,Modifier的首字母縮寫。是一種CSS的命名規(guī)范。定義了CSS中class的命名規(guī)則,如.b-blockname__element_name--modifier-name。其中以b-開頭的原因稍后會解釋。
Block 代表一個完整獨立的實體
試想一下js中的組件,我們將一種特定可復(fù)用的結(jié)構(gòu)封裝成組件,然后我們可以在任何需要的地方使用該組件標(biāo)簽占位,然后就能將一大段復(fù)雜結(jié)構(gòu)應(yīng)用到該地方。
BEM中的block也是類似的原理,Design中的某些設(shè)計元素可以被用在多個地方,就可以把這個設(shè)計元素的樣式提取成一個獨立的塊,而這個塊的名字就是.b-blockname__element-name--modifier-name中blockname的部分。對于class命名來說,這個block就相當(dāng)于命名空間。
舉個例子:
//html
<div class="b-popup-modal">
<h1 class="b-popup-modal__title">title</h1>
<div class="b-popup-modal__body">
body content
</div>
<div class="b-popup-modal__footer">
<button class="b-btn b-btn--primary b-btn--disable">Submit</button>
<button class="b-btn b-btn--default">Cancel</button>
</div>
</div>
彈窗可以作為網(wǎng)頁設(shè)計中一個獨立元素,所以我們可以把獨屬于它的樣式單獨提出來封裝到一個css樣式塊,這個塊的class命名中blockname的部分就是popup。
Element 代表Block里的局部元素
Element指代存在于某個特定block塊內(nèi)部的元素,它可以是block中任意的某個DOM節(jié)點。以上面popup的代碼為例,h1就是一個element,而我們在給它的class命名中以b-popup-modal__title中的__title來表示BEM規(guī)則中E的部分。
Modifiers標(biāo)示了同一個Blcok/Element不同狀態(tài)下的不同樣式
Modifier可以概括為一種狀態(tài)標(biāo)志,對于同一個Block/Element可能有一種基礎(chǔ)樣式,但是在不同的狀態(tài)下會呈現(xiàn)出另一種樣式,這種所謂的狀態(tài)就可以通過命名modifier來區(qū)分。以上面代碼中的.b-btn--disable為例,--disable就是一個modifier,同樣的--primary, --default也是一種modifier。通過定義這樣不同的modifier,我們可以很容易估計某個元素會有什么樣式或者處于什么狀態(tài)。
需要注意的是,所以我們可以單獨定義一個block作為class,但element和modifier都是和block綁定的非獨立模塊,block是他們的命名空間,所以element/modifier一定是和block綁定出現(xiàn)的。
使用BEM編寫CSS
在了解了BEM的基本原理后,接下來再具體說一下它的實現(xiàn)方法。上面已經(jīng)說到BEM實際上是一種命名規(guī)范,在css里就是指class的命名規(guī)范。具體實現(xiàn)規(guī)則團(tuán)隊可以根據(jù)自己的需求做些微調(diào),但原理都離不開block,element和modifier三部分。以下提供一種通用規(guī)則作為參考:
Block,element和modifier名字可以包含字母,數(shù)字,下劃線(_)和短橫杠(-),其中
-可以作為單詞分隔符代替駝峰式命名方式(不采用駝峰式命名的原因是這里html中大小寫不敏感)。Block的命名可以以
b-開頭,因為一個網(wǎng)頁中不是所有的元素都需要被定義成block,b-開頭可以區(qū)分block和普通css class的區(qū)別。例如上面提到的.b-popup-modal,.b-btn。用雙下劃線(__)來指示接下來是一個element名字,例如上面
b-popup-modal__title中的__title。同樣的還有__body,__footer。-
通過雙短橫杠(--)來指示modifier的名字。
不是每一個Block/Element都需要定義Modifier,這完全取決于實際需求。
modifier的位置既可以在block后面,也可以跟在element后面。例如
.b-btn--disable,.b-nav__nav-item--visited。對于跟在block后的modifier,后面也可以再追加element,如
.b-btn--disable__icon。
為什么使用BEM
說了這么多之后,最核心的問題還是為什么要使用BEM,它有什么好?
-
更語意化,可讀性更強
通過雙下劃線(__), 雙橫杠(--) 等符號代碼維護(hù)者可以輕松理解每一部分的意義,更強的可讀性往往意味著更低的維護(hù)成本。
-
模塊化,減少層疊帶來的樣式覆蓋的問題
Block是完全獨立的存在,其內(nèi)部的element/modifier的樣式都在這個block的命名空間下書寫的,所以不會收到其他外部樣式的影響,不存在樣式覆蓋的問題。
.b-popup-modal { //... } .b-popup-modal__title { //... }以上面代碼為例,無論是block本身或者block內(nèi)部element/modifier,都是針對單個class寫樣式,不需要使用復(fù)雜的css選擇器(
.b-popup-modal > .b-popup-modal__title),最大程度地避免了權(quán)重計算。去除掉繼承關(guān)系,css權(quán)重的影響,以block來分的模塊化反而體現(xiàn)得更明顯。 -
增強樣式的重用性
就像js組合不同的組件得到更復(fù)雜的組件一樣,我們也可以通過組合不同的block得到更復(fù)雜的樣式,例如使用
.b-btn,.b-input來組合一個簡單的form樣式,從而提高代碼的可復(fù)用性,從另一方面講也是降低了維護(hù)成本。 -
更容易做項目遷移
因為block樣式是相對獨立的,如果在其他項目有需要,我們完全可以講某個單獨的block相關(guān)的樣式應(yīng)用到其他項目中。
寫在最后
綜上所訴BEM的特點和用法,通過遵循這樣一種命名規(guī)范確實能夠幫助項目維護(hù)一套更易懂,維護(hù)成本相對更低,更容易擴展的CSS代碼庫。
但是一個項目里不是所有的樣式都要做成block。相比于BEM的用法,個人覺得更難的其實是如何精確劃分好每一個block以及對各種modifier的準(zhǔn)確定義。