BEM 是對 CSS 命名的一種規范,推崇將 WEB 頁面模塊化,從而提高代碼的重用度,減少后期維護的成本。原文
快速開始
簡介
BEM(Block,Element,Modifier)是一個基于組件方式的 web 開發方法。
??他的基本思想是將用戶界面分為獨立的模塊。
??即使是一個復雜的 UI 界面,也會使得用戶界面開發簡單而迅速,,并且允許重用現有的代碼,而不是復制粘貼。
內容
- 模塊
- 元素
- 我應該創建一個 Block 還是一個 Element
- 修飾符
- 混合模式
- 文件系統
模塊
一個可重復使用的功能獨立的頁面組件。在 HTML 中,blocks 通過 class
屬性表示。
特征:
- 模塊名稱描述了它的目的(“它是什么?” —— 菜單或者按鈕),而不是它的狀態(“它看起來是什么樣子?” —— 紅色或者大的)。
例如:
<!-- 正確的,這個 'error' 模塊是具有語義上的意義的 -->
<div class="error"></div>
<!-- 不正確的,它描述了模塊的外觀 -->
<div class="red-text"></div>
- 模塊不應該影響它所在的環境,這意味著你不應該為模塊設置會影響到外部的形狀(影響大小的
padding
或邊框)和定位 - 你也不應該在使用 BEM 的時候使用 CSS 標簽選擇器和 ID 選擇器
這些保證了模塊必要的獨立性,可以更好地重用模塊或者將他們從一個地方移動到另一個地方。
模塊使用指南
嵌套關系
- 模塊與模塊之間可以彼此嵌套
- 你可以有任意級別的嵌套層次
例子:
<!-- 'head' 模塊 -->
<header class="header">
<!-- 嵌套 'logo' 模塊 -->
<div class="logo"></div>
<!-- 嵌套 'search-form' 模塊 -->
<form class="search-form"></form>
</header>
元素
是一個模塊的組成部分,且不能脫離模塊單獨地被使用。
特征:
- 元素名稱描述了它的目的(用處)(“這是什么?” —— item,text,等等。),而不是它的狀態(“什么類型的,或者它看起來是什么樣的?” —— 紅色,大的,等等。)
- 完整的元素名的結構是
block-name__element-name
。元素的名字與模塊的名字使用雙下劃線分隔(__)
例子:
<!-- 'search-form' 模塊 -->
<form class="search-form">
<!-- 在 'search-form' 模塊內的 'input' 元素 -->
<input class="search-form__input"/>
<!-- 在 'search-form' 模塊內的 'button' 元素 -->
<button class="search-form__button"></button>
</form>
元素使用指南
- 嵌套關系
- 組成部分
- 可選性
嵌套關系
- 元素之間可以彼此嵌套
- 你可以擁有任意層次的嵌套級別
- 一個元素總是一個模塊的一部分,而不是另一個元素的一部分,這意味著元素的名稱不能被定義為
block__elem1__elem2
這樣的層次結構。
例子:
<!--
正確的。完整的元素名的結構符合如下模式:
'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>
<!--
不正確的。完整的元素名的結構不符合如下模式:
'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 樹中,一個模塊可以有元素嵌套結構:
例子
<div class="block">
<div class="block_elem1">
<div class="block_elem2">
<div class="block_elem3"></div>
</div>
</div>
</div>
然而,在 BEM 的方法論中,這樣的模塊結構通常表示為一個并列的元素列表:
例子
.block {}
.block_elem1 {}
.block_elem2 {}
.block_elem3 {}
這樣在代碼中,你可以在不改變每個單獨的元素的情況下改變一個模塊的 DOM 結構:
例子
<div class="block">
<div class="block_elem1">
<div class="block_elem1"></div>
</div>
<div class="block_elem3"></div>
</div>
模塊的結構改變了,但是元素的規則和他們的名字仍然保持不變。
組成部分
一個元素總是一個模塊的一部分,你不應該單獨使用它。
例子
<!-- 正確的。元素都位于 'search-form' 模塊內 -->
<!-- 'search-form' 模塊 -->
<form class="search-form">
<!-- 在 'search-form' 模塊內的 'input' 元素 -->
<input class="search-form__input" />
<!-- 在 'search-form' 模塊內的 'button' 元素 -->
<button class="search-form__button"></button>
</form>
<!-- 不正確的。元素位于 'search-form' 模塊的上下文之外 -->
<!-- 'search-form' 模塊 -->
<form class=""search-block>
</form>
<!-- 在 'search-form' 模塊內的 'input' 元素 -->
<input class="search-form__input"/>
<!-- 在 'search-form' 模塊內的 'button' 元素 -->
<button class="search-form__button"></button>
可選性
一個元素是一個可選的模塊組件。并不是所有的模塊都必須有元素。
例子:
<!-- 'search-form' 模塊 -->
<div class="search-form">
<!-- 'input' 模塊 -->
<input class="input"/>
<!-- 'button' 模塊 -->
<button class="button"></button>
</div>
我應該創建一個模塊還是一個元素?
- 如果這段代碼可能被重用,并且它不依賴于頁面上的其他組件,那你應該創建一個模塊。
- 如果這段代碼在沒有父實體(模塊)的情況下不能使用,那你應該創建一個元素。
為了簡化開發,元素應該被分割成一小部分-子元素。在 BEM 方法論中,你不能創建元素的元素,在這種情況下,你需要創建一個服務模塊,而不是創建一個元素。
修飾符
一種用于定義模塊和元素的外觀,狀態和行為的實體。
特征:
-
修飾符的名稱描述了它的外觀(“多大?”或者“它的主題是什么?”等等——
size_s
或者theme_islands
),它的狀態(“它與其他有什么不同?” ——disabled
,focused
,等等)以及他的行為(“它的行為什么?”或者“它如何響應用戶?”——比如directions_left-top
)。 - 修飾符的名字與模塊或者元素的名字使用單下劃線分隔(_)
修飾符的類型
Boolean
- 當修飾符的存在或不存在是重要的,與它的值無關時使用這種類型的修飾符。比如:
disabled
。如果一個布爾類型的修飾符是可見的,它的值被假定為true
。 - 修飾符的全名的結構遵循如下模式:
block-name_modifier_name
block-name__element-name_modifier-name
例子
<!-- 'search-form' 模塊有一個 ‘focused’ 的布爾類型的修飾符 -->
<form class="search-form search-form_focused">
<input class="search-form__input"/>
<!-- 'button' 元素有一個 'disabled' 的布爾類型修飾符 -->
<button class="search-form__button search-form__button_disabled">Search</button>
</form>
鍵-值
- 當修飾符的值是重要的使用鍵值對類型。例如:“一個
islands
設計主題的按鈕”:menu_theme_islands
。 - 這種類型的修飾符的全名的結構遵循如下模式:
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>
<!-- 你不能同時使用兩個具有不同值的的相同修飾符 -->
<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>
修飾符使用指南
一個修飾符不能被單獨使用。
從 BEM 的角度,一個修飾符不能脫離模塊或元素而被使用。一個修飾符應該改變實體的外觀,行為或者狀態,而不是替換它。
例子
<!-- 正確的。'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>
[為什么需要在元素或修飾符的名稱上添寫上模塊的名稱?](Why write the block name in the names of modifiers and elements?)
混合模式
一種在單一的 DOM 節點上使用不同 BEM 實體的技術。
混合模式允許你:
- 結合多個實體的行為和樣式,而不是重復編寫代碼
- 在現有代碼的基礎上創建具有新語義的 UI 組件
例子:
<!-- 'header' 模塊 -->
<div class="header">
<!--
將 'header' 模塊的 'search-form' 元素與 'search-form' 模塊混合在一起使用
-->
<div class="search-form header__search-form"></div>
</div>
在這個例子中,我們將 header
模塊的 search-form
元素與 search-form
模塊的行為和樣式結合在一起。這種方式允許我們在 header__search-form
元素上設置額外的形狀和定位,而 search-form
模塊仍然是通用的。因此,我們可以在任何環境中使用模塊,因為模塊沒有指定任何填充。這正是我們可以獨立調用模塊的原因。
文件系統
在 BEM 方法論中采用的組件概念同樣適用于項目的文件結構中。模塊、元素和修飾符的實現可以被分在獨立的文件中,這意味著,我們單獨地使用它們。
特征:
- 一個單獨的模塊對應一個單獨地目錄
- 模塊和其對應的目錄擁有相同的名字。比如,
header
模塊放置在header/
目錄中,menu
模塊放置在menu/
目錄中。 - 一個模塊的實現分為單獨的文件。比如,
header.css
和header.js
。 - 模塊目錄是其元素和修飾所在目錄的根目錄。
- 元素目錄的名稱以雙下劃線(__)開始。比如,
header/__logo
/ 和menu/__item
。 - 修飾目錄的名稱以單下劃線(_)開始。比如,
header_fixed
和menu/_theme_islands/
。 - 元素和修飾的實現分為不同的文件。比如,
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
這樣的文件結構可以很好地支持我們重用代碼。
在生產環境中,這些分支的文件結構將會被組合成共享的文件。
遵循這樣的文件結構并不是必須的。你可以使用任何可替代的項目結構,根據 BEM 原則來組織你的文件結構,比如:
自己英文水平有限,如果文中有錯誤,或者歧異,或者不恰當的地方,希望大家提出來,非常愿意接受大家的批評與建議。