BEM
基于組件方式的web開(kāi)發(fā)方法锈玉,基本思想是將用戶界面分成獨(dú)立的模塊沐兰。
Block(塊)
通常指模塊赏半,組件
Block 是一個(gè)邏輯上和功能上獨(dú)立的頁(yè)面組件贺归,等同于網(wǎng)頁(yè)組件中的部件(等同于網(wǎng)頁(yè)中的組件)。Block 封裝了行為(Javascript)断箫、模板拂酣、樣式(CSS)和其他實(shí)現(xiàn)技術(shù)。獨(dú)立狀態(tài)的 Block 可供復(fù)用仲义,并且促進(jìn)項(xiàng)目開(kāi)發(fā)和維護(hù)婶熬。
內(nèi)容
- 模塊名稱:描述了它的目的(“它是什么?” —— 菜單或者按鈕)埃撵,而不是它的狀態(tài)(“它看起來(lái)是什么樣子赵颅?” —— 紅色或者大的)。
- 模塊不應(yīng)該影響它所在的環(huán)境暂刘,這意味著你不應(yīng)該為模塊設(shè)置會(huì)影響到外部的形狀(影響大小的 padding 或邊框)和定位
- 你也不應(yīng)該在使用 BEM 的時(shí)候使用 CSS 標(biāo)簽選擇器和 ID 選擇器
使用
嵌套關(guān)系
- 模塊與模塊之間可以彼此嵌套
- 你可以有任意級(jí)別的嵌套層次
<!-- 'head' 模塊 -->
<header class="header">
<!-- 嵌套 'logo' 模塊 -->
<div class="logo"></div>
<!-- 嵌套 'search-form' 模塊 -->
<form class="search-form"></form>
</header>
特點(diǎn)
1饺谬、嵌套式的構(gòu)造
Block 可以被嵌套到任何其他 block 里面去。例如鸳惯,一個(gè)頭部 block 可以包含一個(gè) logo商蕴、一個(gè)搜索表單和一個(gè)登錄 block叠萍。
2芝发、隨意放置
Block 可以在一個(gè)頁(yè)面內(nèi)任意移動(dòng),也可以在頁(yè)面之間或項(xiàng)目之間移動(dòng)苛谷。Block 作為獨(dú)立的實(shí)體來(lái)實(shí)現(xiàn)辅鲸,這使得在頁(yè)面上改變 block 的位置 并確保其功能和外觀一切正常 成為可能。
3腹殿、可復(fù)用
一個(gè)界面可以包含同一個(gè) block 的幾個(gè)實(shí)例独悴。
Element(元素)
內(nèi)容
<h2 id="element">元素</h2>
元素(Element)是一個(gè)模塊(block)的組成部分例书,且不能脫離模塊單獨(dú)地被使用。例如刻炒,一個(gè)菜單項(xiàng)(a menu item )不會(huì)在一個(gè)菜單塊(a menu block )范圍之外使用决采,因此它是一個(gè)元素(element)。
- 元素名稱:描述了它的目的(用處)(“這是什么坟奥?” —— item树瞭,text,等等爱谁。)晒喷,而不是它的狀態(tài)(“什么類型的,或者它看起來(lái)是什么樣的访敌?” —— 紅色凉敲,大的,等等寺旺。)
- 完整的元素名的結(jié)構(gòu)是
block-name__element-name
爷抓。元素的名字與模塊的名字使用雙下劃線分隔(__)
使用
1、嵌套關(guān)系
- 元素之間可以彼此嵌套
- 你可以擁有任意層次的嵌套級(jí)別
- 一個(gè)元素總是一個(gè)模塊的一部分迅涮,而不是另一個(gè)元素的一部分废赞,這意味著
元素的名稱不能被定義為 block__elem1__elem2
這樣的層次結(jié)構(gòu)。
<!--
正確的叮姑。完整的元素名的結(jié)構(gòu)符合如下模式:
'block-name__element-name'
-->
<form class="search-form">
<div class="search-form__content">
<input class="search-form__input"/>
<button class="search-form__button"></button>
</div>
</form>
<!--
不正確的唉地。完整的元素名的結(jié)構(gòu)不符合如下模式:
'block-name__element-name'
-->
<form class="search-form">
<div class="search-form__content">
<!-- 推薦:'search-form__input' 或者 'search-form__content-input' -->
<input class="search-form__content__input"/>
<!-- 推薦:'search-form__button' 或者 'search-form__content-button' -->
<button class="search-form__content__button"></button>
</div>
</form>
如果在模塊名稱上定義了命名空間,也要保證元素名稱是依賴于模塊的(block_elem)传透。
在 DOM 樹(shù)中耘沼,一個(gè)模塊可以有元素嵌套結(jié)構(gòu):
<div class="block">
<div class="block_elem1">
<div class="block_elem2">
<div class="block_elem3"></div>
</div>
</div>
</div>
在 BEM 的方法論中,這樣的模塊結(jié)構(gòu)通常表示為一個(gè)并列的元素列表:
.block {}
.block_elem1 {}
.block_elem2 {}
.block_elem3 {}
你可以在不改變每個(gè)單獨(dú)的元素的情況下改變一個(gè)模塊的 DOM 結(jié)構(gòu):
<div class="block">
<div class="block_elem1">
<div class="block_elem1"></div>
</div>
<div class="block_elem3"></div>
</div>
2朱盐、組成部分
一個(gè)元素總是一個(gè)模塊的一部分群嗤,不可以單獨(dú)使用
<!-- 正確的。元素都位于 'search-form' 模塊內(nèi) -->
<!-- 'search-form' 模塊 -->
<form class="search-form">
<!-- 在 'search-form' 模塊內(nèi)的 'input' 元素 -->
<input class="search-form__input" />
<!-- 在 'search-form' 模塊內(nèi)的 'button' 元素 -->
<button class="search-form__button"></button>
</form>
<!-- 不正確的兵琳。元素位于 'search-form' 模塊的上下文之外 -->
<!-- 'search-form' 模塊 -->
<form class=""search-block>
</form>
<!-- 在 'search-form' 模塊內(nèi)的 'input' 元素 -->
<input class="search-form__input"/>
<!-- 在 'search-form' 模塊內(nèi)的 'button' 元素 -->
<button class="search-form__button"></button>
3狂秘、可選性
一個(gè)元素是一個(gè)可選的模塊組件。并不是所有的模塊都必須有元素躯肌。
<!-- 'search-form' 模塊 -->
<div class="search-form">
<!-- 'input' 模塊 -->
<input class="input"/>
<!-- 'button' 模塊 -->
<button class="button"></button>
</div>
①如果這段代碼可能被重用者春,并且它不依賴于頁(yè)面上的其他組件,那你應(yīng)該創(chuàng)建一個(gè)模塊清女。
②如果這段代碼在沒(méi)有父實(shí)體(模塊)的情況下不能使用钱烟,那你應(yīng)該創(chuàng)建一個(gè)元素。
③為了簡(jiǎn)化開(kāi)發(fā),元素應(yīng)該被分割成一小部分-子元素拴袭。在 BEM 方法論中读第,你不能創(chuàng)建元素的元素,在這種情況下拥刻,你需要?jiǎng)?chuàng)建一個(gè)服務(wù)模塊怜瞒,而不是創(chuàng)建一個(gè)元素。
Modifier(修飾符)
Modifier 是一個(gè) BEM 實(shí)體般哼,它定義了一個(gè) block 或 element 的外觀和行為盼砍。
Modifier 可用也可不用(即不一定要用到 modifier)。
Modifier 本質(zhì)上與 HTML 的屬性很相似逝她。同一個(gè) block 會(huì)因?yàn)?modifier 的使用而 看起來(lái)與之前有所不同浇坐。
例如,菜單塊(the menu block )的外觀可能會(huì)因?yàn)樵谒砩嫌昧艘粋€(gè) modifier 而改變黔宛。
內(nèi)容
一種用于定義模塊和元素的外觀近刘,狀態(tài)和行為的實(shí)體。
-
修飾符的名稱:描述了它的外觀(“多大臀晃?”或者“它的主題是什么觉渴?”等等——
size_s
或者theme_islands
),它的狀態(tài)(“它與其他有什么不同徽惋?” ——disabled
案淋,focused
,等等)以及他的行為(“它的行為什么险绘?”或者“它如何響應(yīng)用戶踢京?”——比如directions_left-top
)。 - 修飾符的名字與模塊或者元素的名字使用
單下劃線分隔(_)
修飾符的類型
1宦棺、Boolean
- 當(dāng)修飾符的存在或不存在是重要的瓣距,與它的值無(wú)關(guān)時(shí)使用這種類型的修飾符。比如:disabled代咸。如果一個(gè)布爾類型的修飾符是可見(jiàn)的蹈丸,它的值被假定為 true。
- 修飾符的全名的結(jié)構(gòu)遵循如下模式:
- Modifier(修飾符 )
block-name_modifier_name
block-name__element-name_modifier-name
<!-- 'search-form' 模塊有一個(gè) ‘focused’ 的布爾類型的修飾符 -->
<form class="search-form search-form_focused">
<input class="search-form__input"/>
<!-- 'button' 元素有一個(gè) 'disabled' 的布爾類型修飾符 -->
<button class="search-form__button search-form__button_disabled">Search</button>
</form>
2呐芥、鍵-值
- 當(dāng)修飾符的值是重要的使用鍵值對(duì)類型逻杖。
“一個(gè) islands 設(shè)計(jì)主題的按鈕”:
menu_theme_islands
- 這種類型的修飾符的全名的結(jié)構(gòu)遵循如下模式:
block-name_modifier-name_modifier-value
block-name__element-name_modifier-name_modifier-value
<!-- The `search-form` 模塊有值為 'islands' 的 `theme` 修飾 -->
<form class="search-form search-form_theme_islands">
<input class="search-form__input">
<!-- The `button` 元素有值為 'm' 的 `size` 修飾 -->
<button class="search-form__button search-form__button_size_m">Search</button>
</form>
<!-- 你不能同時(shí)使用兩個(gè)具有不同值的的相同修飾符 -->
<form class="search-form
search-form_theme_islands
search-form_theme_lite">
<input class="search-form__input">
<button class="search-form__button
search-form__button_size_s
search-form__button_size_m">
Search
</button>
</form>
使用
一個(gè)修飾符不能被單獨(dú)使用。
- 從 BEM 的角度思瘟,一個(gè)修飾符不能脫離模塊或元素而被使用荸百。一個(gè)修飾符應(yīng)該改變實(shí)體的外觀,行為或者狀態(tài)潮太,而不是替換它管搪。
<!-- 正確的。'search-form' 模塊有值為 'islands' 的 'theme' 修飾符 -->
<form class="search-form search-form_theme_islands">
<input class="search-form__input">
<button class="search-form__button">Search</button>
</form>
<!-- 不正確的铡买。'search-form' 丟失了 -->
<form class="search-form_theme_islands">
<input class="search-form__input">
<button class="search-form__button">Search</button>
</form>
混合模式:一種在單一的 DOM 節(jié)點(diǎn)上使用不同 BEM 實(shí)體的技術(shù)
混合模式允許
- 結(jié)合多個(gè)實(shí)體的行為和樣式更鲁,而不是重復(fù)編寫(xiě)代碼
- 在現(xiàn)有代碼的基礎(chǔ)上創(chuàng)建具有新語(yǔ)義的UI組件
<!-- 'header' 模塊 -->
<div class="header">
<!--
將 'header' 模塊的 'search-form' 元素與 'search-form' 模塊混合在一起使用
-->
<div class="search-form header__search-form"></div>
</div>
在這個(gè)例子中,我們將 header 模塊的 search-form 元素與 search-form 模塊的行為和樣式結(jié)合在一起奇钞。這種方式允許我們?cè)?header__search-form 元素上設(shè)置額外的形狀和定位澡为,而 search-form 模塊仍然是通用的。因此景埃,我們可以在任何環(huán)境中使用模塊媒至,因?yàn)槟K沒(méi)有指定任何填充。這正是我們可以獨(dú)立調(diào)用模塊的原因谷徙。
文件系統(tǒng):在 BEM 方法論中采用的組件概念同樣適用于項(xiàng)目的文件結(jié)構(gòu)中拒啰。模塊、元素和修飾符的實(shí)現(xiàn)可以被分在獨(dú)立的文件中完慧,這意味著谋旦,我們單獨(dú)地使用它們。
- 一個(gè)單獨(dú)的模塊對(duì)應(yīng)一個(gè)單獨(dú)的目錄
- 模塊和其對(duì)應(yīng)的目錄擁有相同的名字屈尼。比如册着,
header
模塊放置在header/
目錄中,menu
模塊放置在menu/
目錄中脾歧。 - 一個(gè)模塊的實(shí)現(xiàn)分為單獨(dú)的文件甲捏。比如,
header.css
和header.js
鞭执。 - 模塊目錄是其元素和修飾所在目錄的根目錄司顿。
- 元素目錄的名稱以
雙下劃線(__)開(kāi)始
。比如兄纺,header/__logo/
和menu/__item
免猾。 - 修飾目錄的名稱以
單下劃線(_)開(kāi)始
。比如囤热,header_fixed
和menu/_theme_islands/
猎提。 - 元素和修飾的實(shí)現(xiàn)分為不同的文件。比如旁蔼,
header__input.js
和header_theme_islands.css
锨苏。
search-form/ # Directory of the search-form
__input/ # Subdirectory of the search-form__input
search-form__input.css # CSS implementation of the
# search-form__input element
search-form__input.js # JavaScript implementation of the
# search-form__input element
__button/ # Subdirectory of the search-form__button element
search-form__button.css
search-form__button.js
_theme/ # Subdirectory of the search-form_theme modifier
search-form_theme_islands.css # CSS implementation of the search-form block
# that has the theme modifier with the value
# islands
search-form_theme_lite.css # CSS implementation of the search-form block
# that has the theme modifier with the value
# lite
search-form.css # CSS implementation of the search-form block
search-form.js # JavaScript implementation of the
# search-form block
這樣的文件結(jié)構(gòu)可以很好地支持我們重用代碼。
BEM entity(BEM 實(shí)體)
Block,element 和 modifier 合起來(lái)就被成為 BEM entity棺聊。它是一個(gè) 既可以用來(lái)指代單獨(dú)的 BEM 實(shí)體又可以作為 block伞租、element 和 modifier 的總稱的 概念。
Mix(混合體)
Mix 是被托管在(being hosted on)一個(gè)單獨(dú)的 DOM 節(jié)點(diǎn)上的 不同 BEM 實(shí)體(混合而成)的一個(gè)實(shí)例限佩。
Mix允許我們
- 把幾個(gè) BEM 實(shí)體的功能(behavior)和樣式 組合在一起葵诈,同時(shí)避免重復(fù)代碼
- 在現(xiàn)有的 BEM 實(shí)體的基礎(chǔ)上 創(chuàng)建語(yǔ)義上的新界面組件裸弦。讓我們想一下這種 mix 情形:把一個(gè) block 與 另一個(gè) block 的一個(gè) element 組合在一起。
我們假設(shè)作喘,項(xiàng)目里的鏈接(links)通過(guò)一個(gè)鏈接塊(a link block)來(lái)實(shí)現(xiàn)理疙。我們需要把菜單項(xiàng)(menu items )格式化成鏈接(links)。這里有幾種實(shí)現(xiàn)方法:
- 創(chuàng)建一個(gè) 可以把菜單項(xiàng)(item)轉(zhuǎn)變成鏈接(link)的 modifier泞坦。實(shí)現(xiàn)這樣一個(gè) modifier 即必然牽涉到 復(fù)制鏈接塊的功能和樣式窖贤。這樣一來(lái)就會(huì)導(dǎo)致代碼重復(fù)。
- 取一個(gè) 把一個(gè)通用的鏈接塊(link block )與一個(gè)菜單塊的一個(gè)鏈接元素(a link element ) 組合在一起的 mix贰锁。兩個(gè) BEM 實(shí)體的混合體(mix)可以讓我們不用復(fù)制代碼赃梧,就可以使用鏈接塊的基本鏈接功能 和 菜單塊的 CSS 規(guī)則。
BEM tree(BEM 樹(shù) )
BEM tree 是網(wǎng)頁(yè)結(jié)構(gòu)在 block豌熄、element 和 modifier 方面的表示(representation)授嘀。這是一個(gè)在 DOM 樹(shù)之上的抽象概念,它描述了 BEM 實(shí)體的名稱锣险、它們的狀態(tài)粤攒、順序、嵌套和輔助數(shù)據(jù)囱持。在現(xiàn)實(shí)生活中的項(xiàng)目夯接,BEM tree可以呈現(xiàn)在任何支持樹(shù)結(jié)構(gòu)的形式(format)中。
一個(gè)DOM樹(shù)
<header class="header">
<img class="logo">
<form class="search-form">
<input type="input">
<button type="button"></button>
</form>
<div class="lang-switcher"></div>
</header>
BEM tree
header
├──logo
└──search-form
├──input
└──button
└──lang-switcher
在 XML 和 BEMJSON 格式中纷妆,該 BEM tree 則是這樣的
XML
<block:header>
<block:logo/>
<block:search-form>
<block:input/>
<block:button/>
</block:search-form>
<block:lang-switcher/>
</block:header>
BEMJSON
{
block: 'header',
content : [
{ block : 'logo' },
{
block : 'search-form',
content : [
{ block : 'input' },
{ block : 'button' }
]
},
{ block : 'lang-switcher' }
]
}
Block implementation(BEM實(shí)現(xiàn) )
Block implementation 是指一組各不相同的 技術(shù)盔几,這些技術(shù)決定著 BEM 實(shí)體以下幾方面:
- 行為/功能(behavior)
- 外觀
- 測(cè)試
- 模板
- 文檔(documentation)
- 依賴描述
- 附加數(shù)據(jù)(例如:圖片)
Implementation technology(實(shí)現(xiàn)技術(shù))
Implementation technology 是一種用于實(shí)現(xiàn)一個(gè) block 的技術(shù)。Block 可以用一種或多種技術(shù)來(lái)實(shí)現(xiàn)掩幢,例如:
- 行為/功能(behavior)-- JavaScript, CoffeeScript
- 外觀-- CSS, Stylus, Sass
- 模板-- BEMHTML, BH, Jade, Handlebars, XSL
- 文檔(documentation)-- Markdown, Wiki, XML
例如逊拍,如果一個(gè) block 的外觀是用 CSS 來(lái)定義的,這意味著 block 是用 CSS 技術(shù)實(shí)現(xiàn)的际邻。同樣地芯丧,如果一個(gè) block 的文檔是用 Markdown 格式寫(xiě)的,block 就是用 Markdown 技術(shù)來(lái)實(shí)現(xiàn)的世曾。
Block redefinition(塊重定義)
Block implementation 是指通過(guò)在不同的層級(jí)上增加新的功能到 block 來(lái)修改 block缨恒。
Redifinition level(重定義等級(jí))
Redefinition level 是指一組 BEM 實(shí)體和它們的部分實(shí)現(xiàn)。
一個(gè) block 的最終實(shí)現(xiàn) 可以被分成 不同的重定義層級(jí)轮听。每一個(gè)新的層級(jí)都會(huì)擴(kuò)展或覆蓋原始的 block implementation骗露。最終的結(jié)果由 來(lái)自所有按照預(yù)設(shè)的連續(xù)的順序排列的重定義層級(jí)的獨(dú)立的 block implementation technologies 組合而成。
任何 BEM 的實(shí)現(xiàn)技術(shù)都可以被重新定義血巍。
例如萧锉,有一個(gè)連接到項(xiàng)目的第三方庫(kù)。這個(gè)庫(kù)包含現(xiàn)成的 block implementation述寡。該項(xiàng)目指定的 block 保存在一個(gè)另一個(gè)重定義層級(jí)柿隙。比方說(shuō)叶洞,我們需要修改這個(gè)庫(kù)里的某一個(gè) block 的外觀。這并不需要在庫(kù)的源代碼里修改 block 的 CSS 規(guī)則 或者 在項(xiàng)目里復(fù)制代碼禀崖。我們只需在項(xiàng)目里為 那一個(gè) block 創(chuàng)建額外的 CSS 規(guī)則衩辟。在生成過(guò)程中,最終實(shí)現(xiàn)將會(huì)結(jié)合庫(kù)級(jí)別的原有規(guī)則和項(xiàng)目級(jí)別中新的樣式規(guī)則帆焕。