CSS命名規範和模組化的思考

彭薄發表於2018-12-12
之前曾經考慮過這個問題,嘗試過把可能通用的部分都寫在了main.css中,後來發現想起來使用的時候很少。
最近因為頁面中多次設定.btn按鈕樣式,發生了樣式混淆,來來回回改了幾遍,這個對了卻不小心連帶修改了另一個btn按鈕樣式。這次反覆的修改讓我意識到自己的css命名帶來的潛在問題。
我在網上查詢了關於CSS樣式命名和模組化的資料。最後決定寫一份自己的規範以供使用。

比較常見的概念與思想:Sass 、SCSS、LESS、BEM、OOCSS、AMCSS

BEM:

什麼是BEM?

BEM的意思就是塊(block)、元素(element)、修飾符(modifier),是由Yandex團隊提出的一種前端命名方法論。這種巧妙的命名方法讓你的CSS類對其他開發者來說更加透明而且更有意義。BEM命名約定更加嚴格,而且包含更多的資訊,它們用於一個團隊開發一個耗時的大專案。BEM 命名會使得 Class 類名變長,但經過 gzip 壓縮後這個頻寬開銷可以忽略不計。

塊(block):一個獨立的有實際意義的元件或者模組,每個塊在邏輯上和功能上都是相互獨立的。比如button、nav、tab這些,我們在以後的其它專案中也可以直接套用這個元件。對著block我們應該根據功能來命名如btn-warning,不要根據可以變動的樣式來命名btn-red。

元素(element):屬於塊的一部分,它沒有獨立的意義。任何元素在語義上都要繫結到它的塊上。比如鼻子是block,element是DNA。在 BEM 命名規範中,Element 的命名規範是使用 __。如tags__news__items。這種看起來很長的命名規範會給你更好的保障。在scss中,簡化這種寫法可以用&__items。
修飾符(modifier):是塊或元素的標誌。我們可以使用它們來更改其外觀,行為或狀態。tabs__nav__items--blue或者tabs__nav__items--warning。

使用BEM你需要克服對於多餘的醜陋的寫法的不適性。scss改進BEM方法:
  • 這個命名方法可以變形為org-ComponentName--modifiername。我認為在我們的專案裡,這個要更為好寫和美觀。
  • 使用'&'選擇器時我們可以在保持有很好的組織性和可讀性的同時避免巢狀帶來的樣式衝突:

.aboutSection { &__wrapper { max-width 108rem; padding: 3rem 0; }}

複製程式碼

這個模組看上去像是巢狀但並不會帶來巢狀所造成的權重差異等問題。

  • @extend指令,我們不僅可以消除BEM命名的冗餘,而且可以修剪下來你的標記。關於這個建議你向上繼承而不要向下引入:

<li class="nav__item nav__item--active"></li>
&__item { color: white; &--active { @extend .nav__item; border-bottom: 1px solid red; } }複製程式碼
  • 永遠不應該鏈式命名 BEM 元素。 如果你的 class 最終像這樣 .form__row__input是錯誤的。


名稱空間:

我認為這可以應用在我們的專案中,提高了程式碼的可讀性。
  • p-頁面(Page) (應用於body元素的類),對可維護性不是那麼重要的靜態頁面十分有用 —應該避免巢狀使用 (例: p-Homepage);
  • l-佈局(Layout), 比如列(columning),包裹(wrappers) 和容器(containers)等等(例: l-Masthead, l-Footer);
  • c-元件(components )(例: c-Dropdown, c-Button…);
  • u-公共類(Utility classes) — 不會發生改變, 在程式碼的任何地方都不能過載。(例: u-textCenter, u-clearfix…);
  • js-JavaScript鉤子,永遠不應該出現在CSS中。
這些名稱空間暫不使用。
  • is-,has-:表示一種狀態或條件樣式。如 .is-active
  • o-:表示一個物件(Object),如 .o-layout。
  • t-:表示一個主題(Theme),如 .t-light。
  • s-:表示一個上下文或作用域(Scope),如 .s-cms-content。

CSS的scoped實現?

使我們的css具有模組化性質,不會汙染到其他模組。
一直以來,文件上的STYLE元素通常都是作用域全域性的,選擇器按照全域性的CSS優先規則來設定的。要實現區域性的選擇需要先選到容器元素,再用後代選擇器來實現。scoped屬性可以讓STYLE元素不再作用於全域性,而從當前STYLE元素所在的容器開始選擇後代。
但是這種方法只有在火狐瀏覽器才生效,在vue專案中,這種方法的實現是vue在編譯的時候,把帶有scoped屬性的的模組,加上了一個唯一的屬性,然後通過類名+屬性選擇器的方法來實現模組化。
在子元件中使用*{background: red;},測試加scoped和不加scoped的效果即可明白。

CSS究竟該不該巢狀?

過深的巢狀會帶來風險。
scss只是個框架,最終呈現的還是原生css,而你這樣巢狀後最後解析出來的程式碼十分臃腫。並且DOM緊密耦合,你修改一條可能就會影響其他。層級巢狀過度的與頁面中的某一部分耦合了。我們在軟體工程的思想中追求的就是解耦。
不要讓你的巢狀層級多餘三個。巢狀的太多自己看著就蒙逼。(之前我的程式碼從開始使用scss之後,巢狀深如大海……)
在你的結構中不要新增超過兩個類名。如果你需要覆蓋一些樣式的時候請使用有意義的名稱。
在Sass中還提供了一個@at-root 的功能,可以讓你直接跳出去。這樣也可以避免巢狀層級過深。

該不該使用元素標籤?

沒想清楚~

原則:

  1. CSS 命名要求:語義化易於理解,可重用性高,後期維護容易,載入渲染快。
  2. 以英文單詞命名,避免無意義的縮寫,以 - 連線。根據BEM規範,命名格式為block-element--modifier。
  3. 在你的結構中要儘量減少類名。
  4. 不要讓你的CSS巢狀層級多餘三個。
  5. 包裹容器如何命名:container/list - wrap - item - header - content
  6. 如果樣式的命名讓你自己瞄一眼都看不懂又無法改變,請務必註釋。
  7. 所有的<template>中的包裹的最高階唯一元素為<article>,因為在<article>中標籤可以自動補齊,<div>不可以。
  8. 使用有意義的或通用的ID和class命名。ID和class的命名應反映該元素的功能或使用通用名稱,而不要用抽象的晦澀的命名。反映元素的使用目的是首選;使用通用名稱代表該元素不表特定意義,與其同級元素無異,通常是用於輔助命名;使用功能性或通用的名稱可以更適用於文件或模版變化的情況。


寫在後面:

這個規範特別違背本能,一般來說希望越簡單越方便越短越好,這個都不佔。剛剛寫感覺很彆扭,不過確實非常規整,語義上更容易理解,寫得好的話看到命名就知道是在哪裡了。現在的程式碼就是太亂了,開始嘗試使用一段時間,我的理解是這個規範寫得熟練了應該並不麻煩,而且讓你對自己的程式碼瞭解的更加的清晰,後期元件化完成高重構性會使程式碼有種拼貼的簡單。現在開始嘗試,慢慢改進然後確定。


相關文章