BEM方法介紹
已發表: 2022-03-11什麼是 BEM 方法論?
當您構建較小的網站時,如何組織樣式通常不是大問題。 您創建常用文件,編寫所有需要的 CSS,僅此而已。 然而,當涉及到更大、更複雜的項目時,如何組織代碼變得至關重要。 如果您在一個由多個前端和後端開發人員組成的團隊中工作,那麼代碼的結構就更加重要。
今天,有很多方法旨在減少 CSS 代碼並使您的 CSS 代碼更易於維護。 在本文中,我將解釋並提供其中一個示例:BEM。 BEM 代表B lock Element M odifier 。 它背後的主要思想是加快開發過程,並通過將 CSS 類安排到獨立的模塊中來簡化開發人員的團隊合作。 如果你曾經見過像header__form--search
這樣的類名,那就是 BEM。 是的,類可以命名很長,但它們都是可讀和可理解的。
請注意,最佳實踐是僅將 BEM 與類一起使用,而不是 ID,因為類允許您在必要時重複名稱並創建更一致的編碼結構。 此外,如果你想將你的網站分成有組織的模塊,它應該由相同的結構組成:塊、元素和修飾符。 其中每個塊可以有多個元素,並且塊和元素都可以有多個修飾符。 不過,讓我們先從基本的 BEM 結構開始,並通過示例對其進行解釋。
堵塞
塊代表您網站中的一個對象。 將其視為代碼的更大結構塊。 當今每個網站上最常見的塊是頁眉、內容、側邊欄、頁腳和搜索。 BEM 中的塊始終是鏈接 CSS 類的起點。 看幾個塊示例:
- 一個內容
- 一份菜單
- 搜索表格
.content {/* Styles */} .menu {/* Styles */} .search {/* Styles */}
元素
元素是塊中執行特定功能的組件。 它應該只在其塊的上下文中有意義:
- 內容文章
- 一個菜單項
- 搜索輸入字段
.content__article {/* Styles */} .menu__item {/* Styles */} .search__input {/* Styles */}
修飾符
修飾符是我們表示塊的變化的方式。 如果您曾經使用過 Bootstrap,那麼最好的例子就是按鈕大小。 按鈕大小只是按鈕本身的大小變化,這使其成為修飾符:
- 內容精選文章
- 菜單鏈接
- 帶或不帶圖標的搜索字段
.content__article--featured {/* Styles */} .menu__item--link {/* Styles */} .search__input--icon {/* Styles */}
命名約定
BEM 方法的主要目的是使 CSS 選擇器的名稱盡可能信息豐富和透明。 原始 BEM 樣式是這樣定義的:
塊名稱通常是一個單詞,如.header
,但如果你有更長的塊定義,那麼它會用一個連字符-
分隔:
.lang-switcher {/* Styles */}
元素名稱以雙下劃線__
開頭:
.lang-switcher__flag {/* Styles */}
修飾符名稱以單下劃線_
開頭:
.lang-switcher__flag_basic {/* Styles */}
BEM 方法中只有一個非常關鍵的規則——修飾符不能在其所有者的上下文之外使用。
例子:
.btn_big {/* Styles */}
僅當還定義了標頭時才能使用btn_big
。
不好的例子:
<div class=”btn_big”>...</div>
好的例子:
<div class=”btn btn_big”>...</div>
除了這些原始 BEM 樣式之外,還有其他命名方案,例如 Harry Roberts 和 CamelCase 樣式。
Harry Roberts 風格示例:
.block-name__element-name--modifier-name {/* Styles */}
CamelCase 樣式示例:
.BlockName__ElementName_ModifierName {/* Styles */}
其他的也很少,但這兩個是最常見的。 就個人而言,我是 Harris Roberts 提出的命名約定的粉絲,它有以下規則:
- 名字是小寫的
- BEM 實體名稱中的單詞用連字符分隔
-
- 元素名稱與塊名稱之間用雙下劃線
__
分隔 - 布爾修飾符由雙連字符分隔
--
- 不使用鍵值類型修飾符
這種命名約定比其他命名約定更好的原因是您可以很容易地將修飾符元素與其他元素區分開來。 在最初的命名約定中,修飾符的定義如下:
.block__element_modifier {/* Styles */}
但正如你所見,單下劃線和雙下劃線並沒有太大區別。 另一方面,雙連字符提供了清晰的分隔,您可以立即看到修飾符:
.block__element--modifier {/* Styles */}
不同格式的 BEM 示例
請注意,除了 CSS,BEM 在組織 JSON、XML、樹文件或任何支持嵌套的格式方面也非常有用。 將 BEM 方法視為構建 UI 的好方法。
讓我們考慮以下以 BEM 格式構造的 HTML:
<header class=”header”> <img class=”header__logo”> <form class=”header__search-from”> <input class=”header__search-from__input” type=”input”> <button class=”header__search-from__button” type=”button”> </form> <div class=”header__lang-switcher”></div> </header>
使用 JSON 和 XML 格式也可以實現相同的目的。
XML:
<block:header> <block:logo/> <block:search-from> <block:input/> <block:button/> </block> <block:lang-switcher/> </block>
JSON:
{ block: 'header', content: [ { block: 'logo' }, { block: 'search-form', content: [ { block: 'input' }, { block: 'button' } ] }, { block: 'lang-switcher' } ] }
BEM 項目的文件系統組織
在 BEM 中,以正確的方式組織文件至關重要。 BEM 不僅為您提供了一個很好的 CSS 類組織並使它們完全可以理解,而且還為您提供了一個非常可維護的文件結構。 讓我們舉一個示例項目,使用 BEM 文件組織技術和 SASS 文件:
blocks/ input/ __box/ --big/ input__box--big.scss input__box.scss button/ --big/ button--big.scss
正如您在上面看到的,僅通過查看主文件夾中的子文件夾結構,一切都清晰有序。 這樣,誰在你之後工作或你是否在某人之後工作都沒有區別,因為遵循相同的模式非常容易。
將 BEM 項目劃分為平台
除了使用 BEM 方法技術來組織文件之外,您還可以處理更具體的事情。 例如,如果您正在構建一個將完全響應的 Web 項目,並且客戶端指定移動設備上的某些塊與桌面設備上的完全不同,那麼最好將您的 BEM 文件夾結構劃分為平台。 在各種平台上組織按鈕的示例:
common.blocks/ button/ button.scss desktop.blocks/ button/ buttons.scss mobile.blocks/ button/ button.scss
請注意,如果您想使用 BEM 方法組織整個項目,這只是一個示例。 具有 BEM 結構的文件樹不是正確使用 BEM 的強制要求,您可以在項目的某些部分使用 BEM。 到目前為止,我還沒有使用這種嚴格的 BEM 文件結構組織,其中每個元素和修飾符都創建了它的文件。 相反,我只是為具有其元素和修飾符聲明的塊創建一個文件結構。
BEM 實踐
由於您現在熟悉命名約定,我將在實踐中演示 BEM 方法。 假設我們有這個 HTML 代碼在運行:
<a class=”btn btn--big btn--primary-color” href=”#” title=”Title”> <span class=”btn__price”>$3.99</span> <span class=”btn__text”>Product</span> </a>
應用以下 CSS 標記:
.btn__price {/* Styles */} .btn__text {/* Styles */} .btn--big {/* Styles */} .btn--primary-color {/* Styles */}
現在,不要被誤導。 到目前為止,在我們的示例中,我們幾乎總是有一個塊、元素和修飾符,但並不總是如此。
例如,假設我們有一個名為person的塊。 一個人有腿和手,也可以是女性或男性。 如果我們想定義一個有右手的男性,它看起來像這樣:
.person--male__hand--right {/* Styles */}
現在您可以看到 BEM 的真正含義。 我們定義了一個修飾符是性別的人。 因為不管一個人是男是女,它都有一隻手,而手是一個元素。 同樣,每個人都可以擁有右手或左手,這又是一個修飾符。
在另一種情況下,如果我們想用一隻手定義一般人,我們將這樣做:
.person__hand {/* Styles */}
正如您所注意到的,一旦您熟悉了 BEM,就很容易用它來構建您的 CSS 和 HTML 結構。
將 BEM 與 CSS 預處理器一起使用
就個人而言,我無法想像不使用 CSS 預處理器之一來啟動任何新項目。 眾所周知,預處理器是一件很棒的事情,它們為我們提供了很多好處,最重要的是它們與 BEM 方法完美匹配。
在下面的示例中,您可以看到 BEM 與 SASS 結合的最典型示例:
.person { &__hand {/* Styles */} &__leg {/* Styles */} &--male { /* Styles */ &__hand { /* Styles */ &--left {/* Styles */} &--right {/* Styles */} } &__leg { /* Styles */ &--left {/* Styles */} &--right {/* Styles */} } } &--female { /* Styles */ &__hand { /* Styles */ &--left {/* Styles */} &--right {/* Styles */} } &__leg { /* Styles */ &--left {/* Styles */} &--right {/* Styles */} } } }
SASS 代碼將編譯成以下 CSS:

.person__hand {/* Styles */} .person__leg {/* Styles */} .person--male {/* Styles */} .person--male__hand {/* Styles */} .person--male__hand--left {/* Styles */} .person--male__hand--right {/* Styles */} .person--male__leg {/* Styles */} .person--male__leg--left {/* Styles */} .person--male__leg--right {/* Styles */} .person--female {/* Styles */} .person--female__hand {/* Styles */} .person--female__hand--left {/* Styles */} .person--female__hand--right {/* Styles */} .person--female__leg {/* Styles */} .person--female__leg--left {/* Styles */} .person--female__leg--right {/* Styles */}
如果你想更進一步,你可以使用方便的 BEM 的 SASS mixins:
/// Block Element /// @param {String} $element - Element's name @mixin element($element) { &__#{$element} { @content; } } /// Block Modifier /// @param {String} $modifier - Modifier's name @mixin modifier($modifier) { &--#{$modifier} { @content; } }
你可以像這樣使用它:
.person { @include element('hand') {/* Person hand */} @include element('leg') {/* Person leg */} @include modifier('male') { /* Person male */ @include element('hand') { /* Person male hand */ @include modifier('left') { /* Person male left hand */ } @include modifier('right') { /* Person male right hand */ } } } }
這將產生以下 CSS 輸出:
.person__hand { /* Person hand */ } .person__leg { /* Person leg */ } .person--male { /* Person male */ } .person--male__hand { /* Person male hand */ } .person--male__hand--left { /* Person male left hand */ } .person--male__hand--right { /* Person male right hand */ }
我知道你很可能不會有這麼長時間的用例,但這是一個很好的例子,說明瞭如何使用 BEM 以及為什麼它在小型和大型項目中如此強大。
開始您的 BEM 項目
正如官方 BEM 文檔中所解釋的,開始您自己的新 BEM 項目的最簡單方法是使用現有的 GIT 存儲庫。 只需使用 Git 克隆命令:
$ git clone https://github.com/bem/project-stub.git
接下來,轉到新創建的目錄並安裝所有依賴項:
$ npm install
將安裝所有必需的依賴項:
使用 ENB 構建項目:
$ node_modules/.bin/enb make
運行服務器模式進行開發:
$ node_modules/.bin/enb server
結果,出現以下消息:
Server started at 0.0.0.0:8080
現在,這意味著服務器已啟動並正在運行。 您現在可以在此地址上查看結果:
http://localhost:8080/desktop.bundles/index/index.html
如您所見,已經創建了很多元素,這些元素在bemjson
文件中定義,該文件位於此處:
project-stub/desktop.bundles/index/index.bemjson.js
您可以查看和探索生成所有 HTML 的文件的當前結構,您可以在 localhost index.html
文件中看到這些結構。 我們將更改此文件以獲取我們在前一章中解釋過的“Person”BEM 項目。 您可以從index.bemjson.js
文件中刪除(或註釋)整個代碼,並將其替換為以下代碼:
module.exports = { block: 'page', title: 'Person BEM', favicon : '/favicon.ico', head : [ { elem : 'meta', attrs : { name : 'description', content : '' } }, { elem : 'meta', attrs : { name : 'viewport', content : 'width=device-width, initial-scale=1' } }, { elem : 'css', url : 'index.min.css' } ], scripts: [{ elem : 'js', url : 'index.min.js' }], content: [ { block: 'person', content: [ { elem: 'male', content: [ { elem: 'leg', mods: {side: 'left'}, content: 'Male person leg -- left' }, { elem: 'leg', mods: {side: 'right'}, content: 'Male person leg -- right' }, { elem: 'hand', mods: {side: 'left'}, content: 'Male person hand -- left' }, { elem: 'hand', mods: {side: 'right'}, content: 'Male person hand -- right' } ] }, { elem: 'female', content: [ { elem: 'leg', mods: {side: 'left'}, content: 'Female person leg -- left' }, { elem: 'leg', mods: {side: 'right'}, content: 'Female person leg -- right' }, { elem: 'hand', mods: {side: 'left'}, content: 'Female person hand -- left' }, { elem: 'hand', mods: {side: 'right'}, content: 'Female person hand -- right' } ] }, ] } ] };
現在,將生成以下 HTML:
<div class="person"> <div class="person__male"> <div class="person__leg person__leg_side_left"> Male person leg -- left </div> <div class="person__leg person__leg_side_right"> Male person leg -- right </div> <div class="person__hand person__hand_side_left"> Male person hand -- left </div> <div class="person__hand person__hand_side_right"> Male person hand -- right </div> </div> <div class="person__female"> <div class="person__leg person__leg_side_left"> Female person leg -- left </div> <div class="person__leg person__leg_side_right"> Female person leg -- right </div> <div class="person__hand person__hand_side_left"> Female person hand -- left </div> <div class="person__hand person__hand_side_right"> Female person hand -- right </div> </div> </div>
從上面的代碼可以看出,在這個場景中使用了默認的 BEM 編碼方案,因為我們只是使用 BEM 提供給我們的默認設置。 您可以探索和使用更多命令和選項,例如創建新頁面、塊或修改 BEM HTML。 這個我就不深入了,都可以在官方的BEM文檔中找到。
優點和顧慮
優點
- BEM 非常適合維護。 有多少次你不得不在一個大型項目中追隨某人,而你只是太害怕在沒有未知的崩潰的情況下改變任何東西? 使用 BEM 時,您知道元素的確切用途以及它可能出現在哪個塊中。
- 類名合乎邏輯且直觀,團隊中的每個成員都知道該元素在網站上的作用。 BEM 為項目中的每個人提供了一種可以共享的聲明性語法,因此他們在同一頁面上。
- BEM 消除了嵌套的 CSS 選擇器。 每一個 HTML 元素都有自己的 CSS 類,通過它的名字你就知道它的用途是什麼。 一個選擇器來統治它們。
關注點和常見錯誤
- 不要太深入嵌套。 主要規則應該是不要使用超過兩個級別的父級和子級。
- 小心從哪裡開始你的塊作用域。 這裡的一個常見錯誤是開發人員使用塊時,但他沒有意識到在開發的後期同一塊將具有主父塊,這可能會破壞嵌套規則。
- 避免使用 SASS @extend。 引用哈里·羅伯茨的話:
通過不在 Sass 中將類“捆綁”在一起,您可以在視圖中創建更多的組合。 HTML 有更好的書面記錄,因為您可以看到每個類都作用於 DOM 的一部分。 您的 CSS 保持更精簡,因為您不必在每次想要創建新的 UI 時創建新的佔位符類(或組合它們的清單類)。
結論
當我第一次看到BEM編碼方案時,我的第一個想法是:
這些課程太長了,無法編寫和閱讀。
但是在嘗試過之後,現在我無法想像不使用它就開始一個新項目。 對我來說,BEM 極大地提高了我的代碼可維護性,我可以肯定地說,每個將被“投入”到基於 BEM 的項目中的開發人員都會很快趕上整個代碼結構。
儘管如此,社交網絡上還是有很多關於 BEM 的討論。 有人說 BEM 不好,想知道他們為什麼要編寫這麼長的名稱類而不是只使用默認的 HTML 嵌套元素。 好吧,沒有人說你必須喜歡 BEM,但事實是大多數前端開發人員都接受它並發現它非常有用。