[譯] 編寫整潔 CSS 程式碼的黃金法則

檻外畸人發表於2017-03-23

編寫整潔 CSS 程式碼的黃金法則

要編寫整潔的 CSS 程式碼,有一些規則是應當極力遵守的,這有助於寫出輕量可複用的程式碼:

  • 避免使用全域性選擇器和元素選擇器
  • 避免使用權重(specific)過高的選擇器
  • 使用語義化類名
  • 避免 CSS 和標籤結構的緊耦合

本文將依次闡述上述規則。

避免使用全域性選擇器

全域性選擇器包括通配選擇器(*)、元素選擇器(例如pbuttonh1等)和屬性選擇器(例如[type=checkbox]),這些選擇器下的 CSS 屬性會被應用到全站所有符合要求的元素上,例如:

button {
  background: #FFC107;
  border: 1px outset #FF9800;
  display: block;
  font: bold 16px / 1.5 sans-serif;
  margin: 1rem auto;
  width: 50%;
  padding: .5rem;
}複製程式碼

這段程式碼看似無傷大雅,但如果我們需要一個樣式不同的 button 呢?假設需要一個用於關閉對話方塊元件的 .close button:

<section class="dialog">
  <button type="button" class="close">Close</button>
</section>複製程式碼
注意: 為什麼不使用 dialog 元素?

現在,需要編寫 CSS 程式碼來覆蓋那些不需要繼承於 .button 的屬性:

.close {
  background: #e00;
  border: 2px solid #fff;
  color: #fff;
  display: inline-block;
  margin: 0;
  font-size: 12px;
  font-weight: normal;
  line-height: 1;
  padding: 5px;
  border-radius: 100px;
  width: auto;
}複製程式碼

除此之外,還需要編寫大量類似程式碼來覆蓋瀏覽器的預設樣式。但如果將元素選擇器 button 用類選擇器 .default 來替代會如何呢?顯而易見,.close 不再需要指定displayfont-weightline-heightmarginpaddingwidth等屬性,這便減少了 23% 的程式碼量:

.default {
  background: #FFC107;
  border: 1px outset #FF9800;
  display: block;
  font: bold 16px / 1.5 sans-serif;
  margin: 1rem auto;
  width: 50%;
  padding: .5rem;
}

.close {
  background: #e00;
  border: 2px solid #fff;
  color: #fff;
  font-size: 12px;
  padding: 5px;
  border-radius: 100px;
}複製程式碼

還有一點同樣重要:避免使用全域性選擇器有助於減少樣式衝突,即某個模組(或頁面)的樣式不會意外地影響到另一個模組(或頁面)的樣式。

對於重置和統一瀏覽器預設樣式,全域性選擇器完全適用;但對於其他大部分情況而言,全域性選擇器只會造成程式碼臃腫。

避免使用權重過高的選擇器

保持選擇器的低權重是編寫輕量級、可複用和可維護的 CSS 程式碼的又一關鍵所在。你可能記得什麼是權重,元素選擇器的權重是 0,0,1,而類選擇器的權重則是 0,1,0

/* 權重:0,0,1 */
p {
  color: #222;
  font-size: 12px;
}

/* 特殊性:0,1,0 */
.error {
  color: #a00;
}複製程式碼

當為元素選擇器加上一個類名後,該選擇器的優先順序就會高於一般的選擇器。沒有必要將類選擇器和元素選擇器組合在一起來提升優先順序,這樣做會提升選擇器的權重和增加檔案體積。

換句話說,沒有必要使用 p.error 這樣的選擇器,因為僅僅一個 .error 就能達到同樣的效果;此外 .error 還可以被其他元素所複用,而 p.error 則會將 .error 這個類限制於 p 元素上。

避免連結類選擇器

還需要避免連結類選擇器。形如 .message.warning 這樣的選擇器權重為 0,2,0。越高的權重意味著越難進行樣式覆蓋,而且這種連結還會造成其他副作用。例如:

message {
  background: #eee;
  border: 2px solid #333;
  border-radius: 1em;
  padding: 1em;
}
.message.error {
  background: #f30;
  color: #fff;
}
.error {
  background: #ff0;
  border-color: #fc0;
}複製程式碼

如下圖所示,在上述 CSS 的作用下,<p class="message"> 會得到一個帶有深灰色邊框和灰色背景的盒子。

[譯] 編寫整潔 CSS 程式碼的黃金法則

<p class="message error"> 卻會得到 .message.error 的背景和 .error 的邊框:

[譯] 編寫整潔 CSS 程式碼的黃金法則

要想覆蓋連結在一起的類選擇器的樣式,只能使用權重更高的選擇器。在上例中,要想讓邊框不是黃色就需要在已有選擇器上再加一個類名或一個標籤選擇器: .message.warning.exceptiondiv.message.warning。更好的做法是建立一個新類。如果你發現你正在連結選擇器,那就該回過頭重新考量了:要麼是設計上存在不一致的地方,要麼就是過早嘗試避免那些尚不存在的問題。解決這些問題將會帶來更高的可維護性和可複用性。

避免使用 id 選擇器

在一個 HTML 文件中一個 id 只能對應一個元素,因此應用於 id 選擇器的 CSS 規則是很難複用的。這樣做一般都會涉及到一系列的 id 選擇器,例如 #sidebar-features#sidebar-sports

此外,id 選擇器具有很高的權重,要想覆蓋它們就必須使用更“長”的選擇器。例如下面這段 CSS 程式碼,為了覆蓋 #sidebar 的背景顏色屬性,必須使用 #sidebar.sports#sidebar.local

#sidebar {
  float: right;
  width: 25%;
  background: #eee;
}
#sidebar.sports  {
  background: #d5e3ff;
}
#sidebar.local {
  background: #ffcccc;
}複製程式碼

改用類選擇器,例如 .sidebar,可以簡化 CSS 選擇器:

sidebar {
  float: right;
  width: 25%;
  background: #eee;
}
.sports  {
  background: #d5e3ff;
}
.local {
  background: #ffcccc;
}複製程式碼

.sports.local 不僅節省了好幾個位元組,還可以複用到其他元素上。

使用屬性選擇器(例如 [id=sidebar])可以解決 id 選擇器高權重的問題,儘管其複用性不如類選擇器,但其低權重可以讓我們避免使用鏈式選擇器。

注意: id 選擇器的高權重也確有用武之地

在某些情況下,你可能確實需要 id 選擇器的高特殊性。例如,一些媒體站點可能需要其所有子站都使用同樣的導航條元件,該元件必須在所有站點都表現一致並且其樣式是難以被覆蓋的。此時,使用 id 選擇器就可以減少導航條樣式被意外覆蓋的情況。

最後,再來討論一下形如 #main article.sports table#stats tr:nth-child(even) td:last-child 這樣的選擇器。這條選擇器不僅長的離譜,而且其權重為 2,3,4,也很難複用。試想 HTML 中會有多少標籤真能匹配這一選擇器呢?稍作思考,就可以將上述選擇器其縮減為 #stats tr:nth-child(even) td:last-child,其權重也足夠滿足需求了。但還有更好的方法既能提高複用性又能減少程式碼量,也就是使用類選擇器。

注意:前處理器巢狀綜合症

權重過高的選擇器大多源於前處理器中過多的巢狀(譯註:此處所指應是 Sass 中選擇器巢狀過深)。

使用語義化類名

所謂語義化,是指要有意義 —— 類名應當能夠表明其規則有何作用或會作用於哪些內容。此外類名也要能夠適應 UI 需求的變化。命名看似簡單,實則不然。

例如,不要使用 .red-text.blue-button.border-4px.margin10px 這樣的類名,這些類名和當前的設計耦合得太緊了。用 class="red-text" 來修飾錯誤資訊看似可行,但如果設計稿發生了變化並要求將錯誤資訊用橙底黑字表示呢?這時原有類名就不準確了,使人難以理解程式碼的真正含義。

在這個例子中,最好使用 .alert.error 或是 .message-error 這樣的類名,這些類名錶明瞭該如何使用它們以及它們會影響哪些內容(即錯誤資訊)。對用於頁面佈局的類名,不妨加上 layout-grid-col-l- 等字首,使人一眼可以看出它們的作用。之後關於 BEM 方法論的章節詳細闡述了這一過程。

避免 CSS 和標籤結構的緊耦合

你可能在程式碼中使用過子元素選擇器和後代選擇器。子元素選擇器形如 E > F,其中 F 是某個元素,而 E 是 F 的直接父元素。例如,article > h1 會影響 <article><h1>Advanced CSS</h1></article> 中的 h1 元素,但不會影響 <article><section><h1>Advanced CSS</h1></section></article> 中的 h1 元素。另一方面,後代選擇器形如 E F,其中 F 是某個元素而 E 是 F 的祖先元素。還用上述例子,則那兩種標籤結構中的 h1 元素都會受到 article h1 的影響。

子元素選擇器和後代選擇器本身並沒有問題,實際上它們在限制 CSS 規則的作用域方面確實發揮著很好的作用。但它們也絕非理想之選,因為標籤結構經常會發生改變。

遇到過如下情況的同學請舉手:你為某個客戶編寫了一些模版,並且在 CSS 程式碼中用到了子元素選擇器和後代選擇器,並且大多數都是元素選擇器,即形如 .promo > h2.media h3 這樣的選擇器;後來你的客戶又聘請了一位 SEO 技術顧問,他檢查了你程式碼中的標籤結構並建議你將 h2h3 分別改為 h1h2,這時候問題來了 —— 你必須同時修改 CSS 程式碼。

在上述情況下,類選擇器再一次表現出其優點。使用 .promo > .headline.media .title (或者更簡單一些: .promo-headline.media-title)使得在改變標籤結構的時候無需改變 CSS 程式碼。

當然,這條規則假設你對標籤結構有足夠的控制權,這在面對一些遺留的 CMS 系統的時候可能是不現實的,在這種情況下使用子元素選擇器、後代選擇器和偽類選擇器是適當的同時也是必要的。

PS:更多架構合理的 CSS 規則

Philip Walton 在其 “CSS 架構”一文中討論了相關規則,有關 CSS 架構的更多想法參見 Roberts 的網站 CSS 原則 以及 Nicolas Gallagher 的部落格文章 HTML 語義化及前端架構

接下來將會探討有關 CSS 架構的兩種方法,這兩種方法主要用於提升大規模團隊和大規模站點的開發效率,但對於小團隊來說其實也是十分適用的。

掘金翻譯計劃 是一個翻譯優質網際網路技術文章的社群,文章來源為 掘金 上的英文分享文章。內容覆蓋 AndroidiOSReact前端後端產品設計 等領域,想要檢視更多優質譯文請持續關注 掘金翻譯計劃

相關文章