摘要:一開始,公司推出的,包括了規(guī)范以及其配套構(gòu)建工具。代表的不同狀態(tài)或不同版本。再來看一個(gè)之前用常規(guī)方式命名的的例子這些類名真是太不精確了,并不能告訴我們足夠的信息。
這段時(shí)間在整理前端部分的代碼規(guī)范,前面提到的CSS規(guī)范里面會(huì)涉及到選擇器的命名,就參考BEM的命名規(guī)范,內(nèi)容整理如下,供大家參考,請斧正!如大家有興趣,可移步至CSS編碼規(guī)范
BEM是由Yandex公司推出的一套CSS命名規(guī)范,官方是這么描述它的:
BEM是一種讓你可以快速開發(fā)網(wǎng)站并對此進(jìn)行多年維護(hù)的技術(shù)。
一開始,Yandex公司推出的BEM,包括了規(guī)范以及其配套構(gòu)建工具。如今提到的BEM主要是指其中的規(guī)范,在BEM最新的推廣頁中,對其的描述為:
BEM是一種命名方法,能夠幫助你在前端開發(fā)中實(shí)現(xiàn)可復(fù)用的組件和代碼共享。BEM解決的問題 css的樣式應(yīng)用是全局性的,沒有作用域可言。
考慮以下場景:
場景一:開發(fā)一個(gè)彈窗組件,在現(xiàn)有頁面中測試都沒問題,一段時(shí)間后,新需求新頁面,該頁面一打開這個(gè)彈窗組件,頁面中樣式都變樣了,一查問題,原來是彈窗組件和該頁面的樣式相互覆蓋了,接下來就是修改覆蓋樣式的選擇器...又一段時(shí)間,又開發(fā)新頁面,每次為元素命名都心驚膽戰(zhàn),求神拜佛,沒寫一條樣式,F(xiàn)5都按多幾次,每個(gè)組件都測試一遍...
場景二:承接上文,由于頁面和彈窗樣式?jīng)_突了,所以把頁面的沖突樣式的選擇器加上一些結(jié)構(gòu)邏輯,比如子選擇器、標(biāo)簽選擇器,借此讓選擇器獨(dú)一無二。一段時(shí)間后,新同事接手跟進(jìn)需求,對樣式進(jìn)行修改,由于選擇器是一連串的結(jié)構(gòu)邏輯,看不過來,嫌麻煩,就干脆在樣式文件最后用另一套選擇器,加上了覆蓋樣式...接下來又有新的需求...最后的結(jié)果,一個(gè)元素對應(yīng)多套樣式,遍布整個(gè)樣式文件...
以往開發(fā)組件,我們都用“重名概率小”或者干脆起個(gè)“當(dāng)時(shí)認(rèn)為是獨(dú)一無二的名字”來保證樣式不沖突,這是不可靠的。
理想的狀態(tài)下,我們開發(fā)一套組件的過程中,我們應(yīng)該可以隨意的為其中元素進(jìn)行命名,而不必?fù)?dān)心它是否與組件以外的樣式發(fā)生沖突。
BEM解決這一問題的思路在于,由于項(xiàng)目開發(fā)中,每個(gè)組件都是唯一無二的,其名字也是獨(dú)一無二的,組件內(nèi)部元素的名字都加上組件名,并用元素的名字作為選擇器,自然組件內(nèi)的樣式就不會(huì)與組件外的樣式?jīng)_突了。
這是通過組件名的唯一性來保證選擇器的唯一性,從而保證樣式不會(huì)污染到組件外。
BEM的意思就是塊(block)、元素(element)、修飾符(modifier),是由Yandex團(tuán)隊(duì)提出的一種前端命名方法論。這種巧妙的命名方法讓你的CSS類對其他開發(fā)者來說更加透明而且更有意義。BEM命名約定更加嚴(yán)格,而且包含更多的信息,它們用于一個(gè)團(tuán)隊(duì)開發(fā)一個(gè)耗時(shí)的大項(xiàng)目。
命名約定的模式如下:
.block {} .block__element{} .block--modifier {}
.block 代表了更高級別的抽象或組件。
.block__element 代表.block的后代,用于形成一個(gè)完整的.block的整體。
.block--modifier 代表.block的不同狀態(tài)或不同版本。
BEM的關(guān)鍵是光憑名字就可以告訴其他開發(fā)者某個(gè)標(biāo)記是用來干什么的。 通過瀏覽HTML代碼中的class屬性,你就能夠明白模塊之間是如何關(guān)聯(lián)的:有一些僅僅是組件,有一些則是這些組件的子孫或者是元素,還有一些是組件的其他形態(tài)或者是修飾符。
我們用一個(gè)類比/模型來思考一下下面的這些元素是怎么關(guān)聯(lián)的:
.person {} .person__hand {} .person--female {} .person--female__hand {} .person__hand--left {}
頂級塊是‘person’,它擁有一些元素,如‘hand’。一個(gè)人也會(huì)有其他形態(tài),比如女性,這種形態(tài)進(jìn)而也會(huì)擁有它自己的元素。下面我們把他們寫成‘常規(guī)’CSS:
.person {} .hand {} .female {} .female-hand {} .left-hand {}
這些‘常規(guī)’CSS都是有意義的,但是它們之間卻有些脫節(jié)。就拿.female來說,是指女性人類還是某種雌性的動(dòng)物?還有.hand,是在說一只鐘表的指針(譯注:英文中hand有指針的意思)?還是一只正在玩紙牌的手?使用BEM我們可以獲得更多的描述和更加清晰的結(jié)構(gòu),單單通過我們代碼中的命名就能知道元素之間的關(guān)聯(lián)。BEM真是強(qiáng)大。
再來看一個(gè)之前用‘常規(guī)’方式命名的.site-search的例子:
這些CSS類名真是太不精確了,并不能告訴我們足夠的信息。盡管我們可以用它們來完成工作,但它們確實(shí)非常含糊不清。用BEM記號(hào)法就會(huì)是下面這個(gè)樣子:
從這種CSS的寫法上我們就已經(jīng)知道.media__img 和.media__body一定是位于.media內(nèi)部的,而且.media__img--rev是.media__img的另一種形態(tài)。僅僅通過CSS選擇器的名字我們就能獲取到以上全部信息。
BEM的另外一個(gè)好處是針對下面這種情況:
Welcome to Foo Corp
Foo Corp is the best, seriously!
光從上面的代碼來看,我們根本不明白.media和.alpha兩個(gè)class彼此之間是如何相互關(guān)聯(lián)的?同樣我們也無從知曉.body和.lede之間,或者.img-rev 和.media之間各是什么關(guān)系?從這段HTML(除非你對那個(gè)media對象非常了解)中我們也不知道這個(gè)組件是由什么組成的和它還有什么其他的形態(tài)。如果我們用BEM方式重寫這段代碼:
Welcome to Foo Corp
Foo Corp is the best, seriously!
我們立馬就能明白.media是一個(gè)塊,.media__img--rev是一個(gè)加了修飾符的.media__img的變體,它是屬于.media的元素。而.media__body是一個(gè)尚未被改變過的也是屬于.media的元素。所有以上這些信息都通過它們的class名稱就能明白,由此看來BEM確實(shí)非常實(shí)用。
使用BEM常見問題 1 丑極了!通常人們會(huì)認(rèn)為BEM這種寫法難看。我敢說,如果你僅僅是因?yàn)檫@種代碼看上去不怎么好看而羞于使用它.
那么你將錯(cuò)失最重要的東西。除非使用BEM讓代碼增加了不必要的維護(hù)困難,或者這么做確實(shí)讓代碼更難讀了,那么你在使用它之前就要三思而行了。但是,如果只是“看起來有點(diǎn)怪”而事實(shí)上是一種有效的手段,那么我們在開發(fā)之前當(dāng)然應(yīng)該充分考慮它。是,BEM看上去確實(shí)怪怪的,但是它的好處遠(yuǎn)遠(yuǎn)超過它外觀上的那點(diǎn)瑕疵。
BEM可能看上去有點(diǎn)滑稽,而且有可能導(dǎo)致我們輸入更長的文本(大部分編輯器都有自動(dòng)補(bǔ)全功能,而且gzip壓縮將會(huì)讓我們消除對文件體積的擔(dān)憂),但是它依舊強(qiáng)大。
2. 命名好長?BEM的命名中包含了模塊名,長長的命名會(huì)讓HTML標(biāo)簽會(huì)顯得臃腫。
其實(shí)每個(gè)使用BEM的開發(fā)團(tuán)隊(duì)多多少少會(huì)改變其命名規(guī)范,比如Instagram團(tuán)隊(duì)使用的駝峰式:
.blockName-elementName--modifierName { /* ... */ }
還有單下劃線:
.block-name_element-name--modifierName { /* ... */ }
還有修飾器名用單橫線連接:
.blockName__elementName-modifierName { /* ... */ }
其實(shí)這些對縮短命名沒有多大的幫助,但我們也無需擔(dān)心文件體積的問題,由于服務(wù)端有g(shù)zip壓縮,BEM命名相同的部分多,壓縮下來的體積不會(huì)太大。另外現(xiàn)在都用IDE來編寫代碼了,有自動(dòng)提示功能,也無須擔(dān)心重復(fù)的輸入過長的名字。因?yàn)槊L,我們是不是可以用子代選擇器來代替BEM命名?這樣至少在HTML編寫時(shí),讓HTML標(biāo)簽看起來美觀一點(diǎn)。
3. 什么時(shí)候用BEM?當(dāng)你真正使用BEM的時(shí)候,重要的是,請記住你沒必要真的在每個(gè)地方都用上它。比如:
.caps { text-transform: uppercase; }
這條CSS不屬于任何一個(gè)BEM范疇,它僅僅只是一條多帶帶的樣式。
另一個(gè)沒有使用BEM的例子是:
.site-logo {}
這是一個(gè)logo,我們可以把它寫成BEM格式,像下面這樣:
.header {} .header__logo {}
但我們沒必要這么做。使用BEM的訣竅是,你要知道什么時(shí)候哪些東西是應(yīng)該寫成BEM格式的。因?yàn)槟承〇|西確實(shí)是位于一個(gè)塊的內(nèi)部,但這并不意味它就是BEM中所說的元素。這個(gè)例子中,網(wǎng)站logo完全是恰巧在.header的內(nèi)部,它也有可能在側(cè)邊欄或是頁腳里面。一個(gè)元素的范圍可能開始于任何上下文,因此你要確定只在你需要用到BEM的地方你才使用它。
再看一個(gè)例子:
Lorem ipsum dolor...
在這個(gè)例子里,我們也許僅僅只需要另一個(gè)class,可以叫它.headline;它的樣式取決于它是如何被層疊的,因?yàn)樗?content的內(nèi)部;或者它只是恰巧在.content的內(nèi)部。如果它是后者(即恰巧在.content的內(nèi)部,而不總是在)我們就不需要使用BEM。
然而,一切都有可能潛在地用到BEM。我們再來看一下.site-logo的例子,想象一下我們想要給網(wǎng)站增加一點(diǎn)圣誕節(jié)的氣氛,所以我們想有一個(gè)圣誕版的logo。于是我們有了下面的代碼:
.site-logo {} .site-logo--xmas {}
我們可以通過使用--修飾符來快速地為我們的代碼構(gòu)建另一個(gè)版本。
BEM最難的部分之一是明確作用域是從哪開始和到哪結(jié)束的,以及什么時(shí)候使用(不使用)它。隨著接觸的多了,有了經(jīng)驗(yàn)積累,你慢慢就會(huì)知道怎么用,這些問題也不再是問題。
一開始了解BEM的時(shí)候,可能會(huì)產(chǎn)生誤解,出現(xiàn)以下不正確的命名方式:
分頁組件有個(gè)ul列表名為:page-btn__list,列表里面存放每一頁的按鈕,名為:page-btn__list__item__link,這是不對的。
首先,有悖BEM命名規(guī)范,BEM的命名中只包含三個(gè)部分,元素名只占其中一部分,所以不能出現(xiàn)多個(gè)元素名的情況,所以上述每一頁的按鈕名可以改成:page-btn__btn。
而應(yīng)該如下:
其次,有悖BEM思想,BEM是不考慮結(jié)構(gòu)的,比如上面的分頁按鈕,即使它是在ul列表里面,它的命名也不應(yīng)該考慮其父級元素。當(dāng)我們遵循了這個(gè)規(guī)定,無論父元素名發(fā)生改變,或是模塊構(gòu)造發(fā)生的改變,還是元素之間層級關(guān)系互相變動(dòng),這些都不會(huì)影響元素的名字。
所以即使需求變動(dòng)了,分頁組件該有按鈕還是要有按鈕的,DOM構(gòu)造發(fā)生變動(dòng),至多也就不同元素的增刪減,模塊內(nèi)名稱也隨之增刪減,而不會(huì)出現(xiàn)修改名字的情況,也就不會(huì)因?yàn)槊肿儎?dòng),牽涉到JS文件的修改,或樣式文件的修改。
5. 關(guān)于BEM修飾器BEM修飾器代表著元素的狀態(tài),但有時(shí)候元素的狀態(tài)需要js來控制,此時(shí)遵循規(guī)范沒有任何好處,比如激活狀態(tài),BEM推薦的寫法是:
.block__element { display: none; } .block__element--active { display: block;
當(dāng)用js為該元素添加狀態(tài)時(shí),我們需要知道該元素的名字block__element,這樣我們才能推導(dǎo)出它的激活狀態(tài)為block__element--active,這是不合理的,因?yàn)楹芏鄷r(shí)候我們無法得知元素的名稱,所以這時(shí)候,我們應(yīng)該統(tǒng)一js控制狀態(tài)的類名格式,比如is-active、js-active等等,這些類名只用作標(biāo)識(shí),不予許有默認(rèn)的公共樣式:
.block__element { display: none; } .block__element.is-active { display: block; }6. 關(guān)于原子類(短類)與BEM
BEM可以不需要用到原子類,但是如果已經(jīng)引入了類似Bootstrap的框架,也沒必要強(qiáng)制避免使用原子類,比如pull-right、ellipsis、clearfix等等類,這些類非常實(shí)用,和BEM是可以互補(bǔ)的。
在組件開發(fā)中其實(shí)不推薦使用原子類,因?yàn)檫@會(huì)降低組件的可復(fù)用性。可復(fù)用性的最理想狀態(tài)就是組件不僅僅在不同的頁面中表現(xiàn)一致,在跨項(xiàng)目的情況下,也能夠運(yùn)行良好。如果組件的樣式因?yàn)橐蕾囉谀硯讉€(gè)原子類就要依賴整個(gè)Bootstrap庫,那么組件d 遷移負(fù)擔(dān)就重很多了。
原子類更適合應(yīng)用在實(shí)際頁面中,這是因?yàn)轫撁孀儎?dòng)大而且不可復(fù)用,假設(shè)在header中,我們用到了兩個(gè)組件logo和user-panel(用戶操作面板),兩個(gè)組件分別置于header的左側(cè)和右側(cè),我們可以這么寫:
header可以封裝成一個(gè)模塊,但它復(fù)用程度不高,不能算是組件,所以即使使用原子類也沒有關(guān)系。在項(xiàng)目中,使用原子類之前應(yīng)該考慮一下,這個(gè)場景是否變動(dòng)大而且不可復(fù)用,如果是的話,我們可以放心的使用原子類。
組件應(yīng)該是“自洽的”,其本身就應(yīng)該構(gòu)成了一個(gè)“生態(tài)圈”,也就是說,他幾乎不需要外部供給,自給自足就能夠運(yùn)轉(zhuǎn)下去。
7. 關(guān)于子選擇器子代選擇器的方式是,通過組件的根節(jié)點(diǎn)的名稱來選取子代元素。按照這個(gè)思路,分頁按鈕樣式可以這么寫:
.page-btn { /* ... */ } .page-btn .list { /* ... */ }
HTML看起來美觀多了,但這解決了樣式?jīng)_突問題么?試想下,如果讓你來接手這個(gè)項(xiàng)目,要增加一個(gè)需求,新增一個(gè)組件,你命名放心么?
你面臨的問題是:你打開組件目錄,里面有個(gè)分頁組件,叫做page-btn,可是你完全不知道要怎么給新組件命名,因?yàn)榧词剐陆M件模塊名與page-btn不一樣,也不能保證新組件與分頁組件不沖突。
比如新的需求是“新增一個(gè)列表組件”,如果該組件的名字叫做list,其根節(jié)點(diǎn)的名字叫l(wèi)ist,那么這個(gè)組件下面寫的樣式,就很可能和.page-btn .list的樣式?jīng)_突:
.list { /* ... */ }
這還僅僅只有兩個(gè)組件而已,實(shí)際項(xiàng)目中,十幾個(gè)或幾十個(gè)組件,難道我們要每個(gè)組件都檢查一下來“新組件名是否和以往組件的子元素命名沖突了”么?這不現(xiàn)實(shí)。
BEM禁止使用子代選擇器,以上是原因之一。子代選擇器不好的地方還在于,如果層次關(guān)系過長,邏輯不清晰,非常不利于維護(hù)。為了懶得命名或者追求所謂的“精簡代碼”,寫出下面這種選擇器:
.page-btn button:first-child {} .page-btn ul li a {} /* ... */ /* 維護(hù)代碼,新增需求 */ .page-btn .prev {}
用DOM結(jié)構(gòu)層次關(guān)系結(jié)構(gòu)來定位元素,可能會(huì)因?yàn)樾枨蟾淖兌竺娣e的重寫樣式文件。試想一下維護(hù)這類代碼有多么痛苦,我們要一邊檢查該元素的上下文DOM結(jié)構(gòu),一邊對照著css文件,一一對比,找到該元素對應(yīng)的樣式,也就是說我為了改一個(gè)元素的代碼,需要不斷翻閱HTML文件和CSS文件,可維護(hù)性非常之差。更有甚者,來維護(hù)這塊代碼的同事,直接在樣式文件最后添加覆蓋樣式,這會(huì)造成一個(gè)非常嚴(yán)重的問題了:同一個(gè)元素樣式零散的分布在文件的不同地方,而且定位該元素的選擇器也可能各不相同。
這樣的樣式文件只會(huì)越寫越糟糕,可以說,當(dāng)我們用子代選擇器來定位元素時(shí),這個(gè)樣式文件就已經(jīng)注定是要被翻來覆去的重構(gòu)的了,甚至,每個(gè)來維護(hù)這個(gè)文件的人都會(huì)將其重構(gòu)一遍。
子代選擇器還會(huì)造成權(quán)重過大的問題,當(dāng)我們要做響應(yīng)式的時(shí)候,某個(gè)帶樣式的元素需要適配不同的屏幕,此時(shí),我們還要不斷的確認(rèn)該元素之前的選擇器寫法!為了覆蓋前面權(quán)重過大的樣式,甚至通過添加額外的類名或標(biāo)簽名來增加權(quán)重??上攵?,此后這個(gè)樣式文件的維護(hù)難度就像雪球一樣,越滾越大。
如果我們用的是BEM,要覆蓋樣式很簡單:找到要覆蓋樣式的元素,得知它的類名,在媒體查詢中,用它的類名作為選擇器,寫下覆蓋樣式,樣式就覆蓋成功了,不需要擔(dān)心前面樣式的權(quán)重過大。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/112172.html
摘要:示例庫通過記錄來查看定制類名默認(rèn)的哈希算法是,從前面我們可以發(fā)現(xiàn)被編譯成了這樣的字符串。與上面不加等價(jià)顯式的局部作用域語法通過示例庫的記錄來查看下的樣式復(fù)用對于樣式復(fù)用,提供了組合的方式來處理。 showImg(https://segmentfault.com/img/bV9WfX?w=800&h=274);前端發(fā)展越來越快,這應(yīng)該是每個(gè)前端開發(fā)者的切身感受,但是CSS 是前端領(lǐng)域中進(jìn)...
摘要:父類為,代表著一系列文章的列表。對于可讀性較好地與代碼,不應(yīng)該像一本書,而應(yīng)該像一個(gè)故事,一個(gè)故事中會(huì)存在角色和角色之間的關(guān)系,而這種更多的語義化地可以較好地提示你整個(gè)代碼的可維護(hù)性。無論哪種文件組織方式比較順眼,你都應(yīng)該遵循統(tǒng)一的原則。 原文地址。本文從屬于Web 前端入門與最佳實(shí)踐。 CSS的學(xué)習(xí)是一個(gè)典型的低門檻,高瓶頸的過程,第一次接觸CSS的時(shí)候覺得一切是如此簡單,直到后面越...
摘要:本篇介紹幾種命名規(guī)范。使用的網(wǎng)站四其他命名規(guī)范等減少對結(jié)構(gòu)的依賴增加重復(fù)性的使用幾種命名規(guī)范比較與在命名上相反的點(diǎn)可以放心使用,以為都是在模塊內(nèi)但不推薦當(dāng)前我們的網(wǎng)站略有思想更概括,中的,相當(dāng)于的,相當(dāng)于的,相當(dāng)于的中文 本篇介紹幾種CSS命名規(guī)范。 (規(guī)范詳細(xì)請參考底部References) 一、NEC (nice easy css) 網(wǎng)易前端CSS開源項(xiàng)目 1.1 樣式分類 重...
摘要:預(yù)處理器最大的好處就是可以支持模塊引入,用的方式來編寫,解決了部分混亂以及代碼冗余的問題,但是也不能完全避免。為什么引入CSS Modules 或者可以這么說,CSS Modules為我們解決了什么痛點(diǎn)。針對以往我寫網(wǎng)頁樣式的經(jīng)驗(yàn),具體來說可以歸納為以下幾點(diǎn): 全局樣式?jīng)_突 過程是這樣的:你現(xiàn)在有兩個(gè)模塊,分別為A、B,你可能會(huì)單獨(dú)針對這兩個(gè)模塊編寫自己的樣式,例如a.css、b.css,看...
摘要:針對塊的類名會(huì)加一些前綴,這些前綴在中有類似命名空間的作用。每一個(gè)塊名應(yīng)該有一個(gè)命名空間前綴每一條規(guī)則必須屬于一個(gè)塊。為了避免創(chuàng)建三個(gè)不同的塊,最好是在塊上加修飾符。的源碼當(dāng)中充分實(shí)現(xiàn)了這種命名方式在使用的情況下 在項(xiàng)目的開發(fā)過程當(dāng)中, 我們往往因?yàn)槿找鎻?fù)雜的css代碼而感到力不從心. 如何合理的組織css代碼成為了我們前端開發(fā)過程中必須考慮到的環(huán)節(jié). 在讀element源代碼的時(shí)候,...
閱讀 3256·2021-11-15 11:37
閱讀 2466·2021-09-29 09:48
閱讀 3833·2021-09-22 15:55
閱讀 3033·2021-09-22 10:02
閱讀 2655·2021-08-25 09:40
閱讀 3249·2021-08-03 14:03
閱讀 1712·2019-08-29 13:11
閱讀 1583·2019-08-29 12:49