感謝支持ayqy我的訂閱號,每週義務推送1篇(only unique one)原創精品博文,話題包括但不限於前端、Node、Android、數學(WebGL)、語文(課外書讀後感)、英語(文檔翻譯)
若是以爲弱水三千,一瓢太少,能夠去 http://blog.ayqy.net 看個痛快 css
一.BEM簡介
BEM自稱是前端開發方法論(Frontend Development Methodology),提供了包括命名規則、CSS/JS模塊化原則、構建工具在內的一套用於開發環節的方法前端
P.S.強調開發環節是爲了與Yeoman之類的東西區分開,BEM想說的是方法,而不是工具web
提供了一套命名規則(BEM命名規則),主要用於模塊化CSS,但BEM中也用在了JS以及文件命名等各個方面編程
CSS中主要解決了這些問題:微信
團隊協做中樣式命名(好比class)衝突app
用長
class
名解決,不使用結構化選擇器(如子選擇器、後代選擇器等等),類名自帶層級關係框架實現代碼本身會說話(self-documenting code)的目標模塊化
語義化類名,提供更多的信息,例如元素名、功能、所屬組件名等等svn
避免組件之間相互影響工具
不依賴結構化選擇器,全靠類名
怎樣才能在同一DOM節點上組裝各類特性,同時避免重複代碼(複製粘貼)
Mix,例如
div.classA>div.classB
組合Block A和Block B
這些解決方案在如何寫好CSS都有說明,若是隻是定義了一套長類名格式的話,確實沒什麼意義,因此BEM還爲這一套命名實現了框架和工具,確保開發過程當中能用好用
二.術語概念
(建議跳過,直接看紅色部分,官方解釋等於沒解釋)
Block
邏輯和頁面功能都獨立的頁面組件,是一個可複用單元,特色以下:
能夠隨意嵌套組合
能夠放在任意頁面的任意位置,不影響功能和外觀
可複用,界面能夠有任意多個相同Block的實例
Element
Block的組成部分,依賴Block存在(出了Block就不能用)
Modifier
[可選]定義Block和Element的外觀及行爲,就像HTML屬性同樣,能讓同一種Block看起來不同
BEM entity
上面三個都是
Mix
單一DOM節點上各個BEM entity構成的一個實例,特色以下:
能把幾個BEM entity的行爲、樣式結合起來,避免代碼重複
基於現有BEM entity語義化地建立新的界面組件
BEM tree
用BEM對web頁面結構進行描述
Block implementation
不少不一樣的技術決定了BEM entity的行爲、外觀、測試、模板、文檔、依賴描述、額外數據(例如圖片)等方面
Implementation technology
用來實現一個Block的技術,能夠是一種或者多種
Block redefinition
在不一樣層面上經過給Block添加新特性來修改Block的實現
Redefinition level
一系列BEM entity及其部分實現
使用中能夠看做邏輯層級,例如project level、library level,前者能夠重寫後者Block的功能及外觀
P.S.實際是經過文件目錄和按順序引入實現的層級隔離和重寫
須要關注的點:
B(Block):表示模塊,最小的可複用單元,功能獨立,能夠嵌套、組合使用
E(Element):B的組成部分
M(Modifier):表示E的狀態(不一樣狀態下的E有不一樣的功能和外觀),也是B的組成部分
BEM tree:用BEM術語描述頁面/項目結構
Block implementation:實現Block須要的東西,包括全部相關內容,好比JS,CSS,image等等
Redefinition level:能夠看做邏輯層級,在高層能夠重寫/擴展低層Block(模塊)
三.BEM命名規則
block-name__element-name_mod-name
,例如nav-menu__item_active
block-name
自己可能並不對應樣式,而只做爲Block的邏輯名
BEM只是提供通常方法,並無限定必須使用這種命名規則
四.JavaScript for BEM
命名規則一樣適用於JS
JS中,Modifier用來表達Block或者Element的邏輯(CSS中,Modifier用來定義外觀),JS經過一系列狀態來描述Block和Element的行爲
不經過class
來查找組件,而是經過Block名(HTML中,不僅經過class
惟一標識,也能夠是標籤、屬性等等),例如:
document.querySelector('.button') .addEventListener('click', function() { document.querySelector('.popup').classList.toggle('popup_visible'); }, false); // BEM Style(僞代碼) block('button').click(function() { block('popup').toggleMod('visible'); });
實際應用須要引入i-bem.js
Modifier 能夠設置Block的具體狀態,經過添加和移除Modifier來實現Block的狀態切換,對Modifier的操做會觸發事件,通知相關Block。 爲了確保這套機制正確運行,不該該容許時隨意切換狀態或者直接修改某個DOM節點的class,這些操做必須經過BEM提供的helper來實現(這種限 制相似於svn本地倉庫)
Modifier對應狀態,狀態關聯功能。添加Modifier時,狀態發生變化,自動應用相應功能;移除Modifier時,相應功能會被自動移除
定義Modifier和狀態以及底層之間的關聯:
block('button').onSetMod({ focused: { true: this.onFocus, '': this.onBlur } });
屏蔽了 狀態之下的東西(多是添加/移除Modifier引發狀態改變,或者是用戶行爲觸發的),在編程層面須要關注的最小單元是狀態(Modifier)。可 以經過給Modifier添加樣式來定義各個狀態的外觀,也能夠經過Redefinition level來改變或者徹底重寫Block的行爲
把代碼分離到各文件中,遵循文件目錄結構規則
規則:
每一個Block的邏輯和可選的Element以及Modifier都放在單獨文件裏
每一個組件的JS文件目錄結構遵循BEM文件目錄結構規則
示例:
logo/ logo.css # Block’s appearance logo.tmpl # Templates for generating the block’s HTML representation logo.js # Dynamic behavior of the block in the browser
便於管理、複用、移植、支持Redefinition level和組合使用
把邏輯分離到各個Redefinition level
在不一樣的Redefinition level實現新Block能夠繼承並擴展示有的Block,或者徹底重寫該Block,例如:
// 徹底重寫 block('button').onSetMod({ focused: { true: this.someCustomOnFocused } }); // 部分重寫 block('button').onSetMod({ focused: { true: function() { this.__base.apply(this, arguments); this.someCustomOnFocused(); } } });
原則
JS中的Block與DOM節點沒有強對應關係,好比有的Block沒有DOM表現,只是單純的邏輯塊
Block之間的交互經過事件訂閱來實現,好比:
訂閱其它Block實例的事件
訂閱Modifier變動
直接調用其它Block的公開接口
其它任何交互模式,好比Event Channel(消息機制)
Element只能經過所屬的Block提供的API來訪問,不能直接訪問某個Block中的Element
五.文件目錄結構
代碼被分解成獨立的小部分,便於開發獨立Block,發佈前組裝起來進行優化
特色
把Block implementation分解到各個單獨的文件中
便於管理(開發過程當中)和移植(植入其它項目)
可選的Element和Modifier都存放在各個單獨的文件中
只引入相關的Block implementation(按需加載)
文件按語義分組,而不是文件類型
好比全部Block都放在
block
目錄下,以保證只引入須要的Block項目被劃分紅各Redefinition level
爲了消除重複代碼,便於重寫/擴展示有Library Block,當Library Block更新時,不會影響上層的重寫/擴展
目錄結構示例
blocks/ input/ # input block directory _type/ # type modifier directory input_type_search.css # Implementation of modifier type # with value search in CSS technology __box/ # box element directory input__box.css input.css input.js button/ # button block directory button.css button.js button.png popup/ # popup block directory _target/ popup_target.css # Common code of modifier target popup_target_anchor.css # Modifier target with value anchor popup_target_position.css # Modifier target with value position _visible/ popup_visible.css # Boolean modifier visible popup.css popup.js
文件名也遵循BEM命名規則,Modifier之間的共享部分能夠提出來,做爲單獨文件(popup_target.css
)
Redefinition level示例
library.blocks/ button/ button.css # CSS implementation in the linked library (height 20px) project.blocks/ button/ button.css # Redefinition of CSS implementation (height 24px)
文件存放在不一樣的層級上,分離開避免互相影響
或者按平臺分層:
common.blocks/ button/ button.css # Generic CSS implementation of the button desktop.blocks/ button/ button.css # Desktop platform-specific button features mobile.blocks/ button/ button.css # Mobile platform-specific button features
build過程會自動按層級引入,例如:
@import(common.blocks/button/button.css); /* Generic CSS rules */ @import(desktop.blocks/button/button.css); /* Desktop platform-specific */
六.build
BEM項目都有多級文件結構,build完成的工做是:
把Block相關的單獨文件整合起來
按需引入Block、Element和Modifier
確保按正確順序引入(保證Redefinition level)
固然,須要預先定義一些依賴信息,好比:
建立頁面時列出相關的Block、Element和Modifier
指明頁面中的Block、Element和Modifier的依賴關係
依賴聲明能夠經過工具完成,好比可以自動根據HTML中應用的class,生成BEM Tree,或者根據項目結構生成依賴信息(非按需生成,全部東西都會被包進來,適用於肯定項目目錄下全部東西都是必須的狀況,例如類庫),關於依賴聲明方式的更多信息,請查看Declarations in BEM
依賴引入方案與實現有關,好比,CSS用@import
,JS用AMD等方案
build結果
// build前 blocks/ # Directory containing the blocks bundles/ # Directory containing all build results hello/ # Directory for the hello page hello.decl.js # List of BEM entities required for the hello page An example of a post-build file structure of a BEM project: // build後 blocks/ # Directory containing the blocks bundles/ # Directory containing all build results hello/ # Directory for the hello page hello.decl.js # List of BEM entities required for the hello page hello.css # Built CSS file for the hello page hello.js # Built JavaScript file for thehello page
上例中hello.decl.js
中定義了依賴的組件及其依賴關係
build tool
官方提供的build工具是ENB,聽說很強大:
The BEM platform uses ENB, which is a tool suitable for building BEM projects of any level of complexity.
想要嘗試請查看新手教程:Starting your own BEM project
參考資料
https://en.bem.info/
(居然發現了師父,之後發佈前得預處理一下了。>_<)
本文分享自微信公衆號 - 前端向後(backward-fe)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。