# 第一章 BEM [竹白記事本](https://chupainotebook.blogspot.com/),學習紀錄,2019/05/10。 ###### tags: `BEM 學習筆記` --- ## 1-1 BEM 是什麼? [BEM](https://en.bem.info/) 是由 Yandex 團隊提出來的一種創新命名 Class 名稱的設計模式。 BEM 命名給 CSS 以及 HTML 提供清晰結構,結合命名空間提供更多信息,模組化提高代碼的重用,以達到 CSS 命名語義化、可重用性高、後期維護容易、加載渲染快的要求。 BEM 的名稱源於該方法學的三個組成部分英文字首,分别為: - Block 區塊 - Element 元素 - Modifier 修飾符 這三個部分的結合稱為 BEM 實體。 首先我先來看幾個的例子,以下是我們常見的寫法: ```htmlmixed <div class="product"> <div class="title"></div> <ul class="menu"> <li class="item active"><a href="#"></a></li> <li class="item"><a href="#"></a></li> </ul> </div> ``` ```css .product { /* ... */ } .product .title { /* ... */ } .menu { /* ... */ } .menu .item { /* ... */ } .menu .item .active { /* ... */ } .menu .item a { /* ... */ } ``` 雖然 HTML 看起來美觀,class 名稱有一定語義,但是並不能真正的重用,而且無法從 HTML 結構看出彼此之間的階層關係,一定要查看 CSS 文檔出來查看彼此之間的關係。 而且透過子代選擇器,選取子元素最大的問題在於,之後如果要新增組件,你容易遇到衝突。不知道命名該如何下手,該如何保證新的組件與分頁不衝突。 另一種常見的寫法: ```htmlmixed <div class="main-news-box"> <h2 class="news-title"></h2> <ul class="main-news-list"> <li><a href="#"></a></li> <li><a href="#"></a></li> </ul> </div> ``` 這種寫法的問題: - `.news-title` 在別的地方有沒有定義,能獨立使用還是會與其他地方互相影響。 - `li`、`a` 太依賴 HTML 結構,需要層層嵌套。 但如果使用 BEM 的命名規則,將會如下: ```htmlmixed <div class="product"> <div class="menu"> <li class="menu__item menu__item--active"><a href="#"></a></li> <li class="menu__item"><a href="#"></a></li> </div> </div> ``` 從 HTML 結構上,我們就可以得知,`product` 與 `menu` 沒有關係,而 `active` 只作用在 `li`,而且我們可以很輕易的新增 Element 到 `menu` 中不怕發生衝突。 --- ## 1-2 連接符 在選擇器中,由以下三種符號來表示擴展的關係: - `-` 中線:僅作爲連字符使用,表示某個塊或者某個子元素的多單詞之間的連接記號。 - `__` 雙底線:用來連接塊和塊的子元素。 - `--` 雙中線:用來描述一個塊或者塊的子元素的一種狀態。 ```css .block-namee-element-name--modifier-name { /* ... */ } ``` BEM 是提供一種規範,而具體命名規則可以根據個人偏好選擇: ### 駝峰式 ```css .blockName-elementName--modifierName { /* ... */ } ``` ### 單下底線 ```css .block-name_element-name--modifierName { /* ... */ } ``` ### 修飾器名用單下底線 ```css .blockName__elementName-modifierName { /* ... */ } ``` 任何一種規範,都是基於實際需求而定,便於團隊開發和維護擴展,每個規範都是經過合理評估後所得出的一種「思路」和「建議」。 我個人是比較常使用 `Block__Element_Modifier` 自訂規則。 --- ## 1-3 Block 區塊 Block 指的是 Web 應用開發中的模組。每個 Block 在邏輯和功能上都是相互獨立具備自己特有的意義。 在大多數情況下,任何獨立的頁面元素(或複雜或簡單)都可以被視作一個區塊。它的 HTML 容器會有一個唯一的 class 名稱,也就是這個區塊的名字。 基本原則與說明: - 不能使用 CSS 標籤選擇器和 ID 選擇器。 - Block 名稱需能清晰的表達出,其用途、功能或意義,具有唯一性。 - Block 名稱前,可以加入一些簡短的前綴來達到命名空間的效果,關於命名空間下面章節會提到。 - 每個塊在邏輯上和功能上都相互獨立,在頁面上不能相依其他 Blocks 或元素。 - 不要定義過多影響所在環境的外部樣式(例如 `margin`),確保塊在不同地方復用和嵌套時,增加其擴展性。 - 由於塊是獨立的,可以在應用開發中進行復用,從而降低代碼重複並提高開發效率。 - 塊可以放置在頁面上的任何位置,也可以互相嵌套。 --- ## 1-4 Element 元素 Element 為 Block 的一部分並且相依於 Block 的意義,簡單來說就是,如果一個區域不能拿到外部單獨使用,那麼就應該作為一個 Element。 基本原則與說明: - Element 名稱需能簡單的描述出,其結構、佈局或意義,並且在語義上與 Block 相關聯。 - 不能與 Block 分開單獨使用,並且在頁面上不能相依於其他的 Block 或 Element。 - Element 始終是 Block 的一部分,而不是另一個 Element。這表示 Element 名稱無法定義層次結構,例如 `block__elem1__elem2`,之後我們會提到為什麼不行。 - Block 的內部元素,都被認為是 Block 的子元素。 - Element 和 Element 之間可以彼此嵌套。 --- ## 1-5 Modifier 修飾符 Modifier 是定義 Block 和 Element 的外觀、狀態或類型。 基本原則與說明: - 需能直觀易懂表達出,其外觀、狀態或行為。 - 不能脱離 Block 或 Element 使用。 - 應該改變的是實體的外觀,行為或狀態,而不是替換它。 - 值可以是 Boolean 或 Key-value 形式。 - 不能同時使用兩個具有不同值的相同 Modifier。 一般來講,如果修飾符的值可有可無(Boolean 形式),舉例來說: ```htmlmixed <button class="button button--active">Button</button> ``` 沒加上 `button--active` 就是原始樣式。 如果 Modifier 的值可以有多種狀態,則使用 Key-value 形式。 就是將其擴展,範例: ```css .btn--size--lg { } .btn--size--m { } .btn--size--s { } .search-form--theme--dark { } .search-form--theme--light { } ``` --- ## 1-6 總結 ### 1. 切換到 BEM 式 CSS - 把 DOM 模型扔到一邊,學習創造 Block。 - 不要使用 ID 選擇器和標籤選擇器。 - 最小化選擇器嵌套。 - 使用 class 命名約定來避免命名衝突,確保選擇器名稱具備自解釋性。 - 用 Block、Element 和 Modifier 的方式工作。 - 把可能會改變的 CSS 樣式屬性從 Block 挪到 Modifier 裡去。 - 把代碼拆分成獨立的部分從而更容易的使用單獨的 Block。 - 重複使用 Block。 - 注意,不要走火入魔過度模組化,適當拿捏,不然 HTML 會很恐怖。 ### 2. BEM 的優缺點 **優點** - 語義化,此處的語義化並非 HTML 標籤的語義化,對 SEO 可能也沒有任何意義,但閱讀上非常明瞭,可以直接從 HTML 結構就能看出階層關係。 - 減少選擇器層層嵌套,有利於渲染效率。 - 不像 OOCSS 它並不是為了處理關於 CSS 全部的模組化,反倒是像是命名空間的概念,透過 class 名稱建立各自獨立的 CSS 模組,並且不會互相干擾,一定程度上,避免命名的污染。 **缺點** - BEM 的一個槽點是,命名方式長而難看,書寫不雅,很多人討厭 BEM 就是因為 HTML 會很醜。但相比 BEM 格式帶來的便利來說,我們應客觀看待。 - 類名與命名空間互相依賴,命名空間名稱需要重新變更所有類名,但可使用 CSS 預處理器改善這個缺點。 ### 3. BEM 解決的問題 由於 CSS 的樣式應用是全局性,沒有作用域可言。 考慮以下場景: 1. 開發一個彈窗組件,在現有頁面中測試都沒問題,一段時間後,但之後新增頁面時,彈窗組件中的樣式卻跑版了,查看原因發現頁面樣式與組件樣式互相衝突。 2. 承接上文,為了防止衝突,在選擇器加上一些結構邏輯,比如子選擇器、標籤選擇器,借此讓選擇器獨一無二,但之後新增頁面又發生衝突,又繼續層層疊疊,最後使得整個文件失控難以管理。 BEM 解決這一問題的思路在於,由於項目開發中,每個組件都是唯一無二的,其名字也是獨一無二的,組件內部元素的名字都加上組件名,並用元素的名字作為選擇器,自然組件內的樣式就不會與組件外的樣式衝突了。