天天看點

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

<b>本文講的是[譯] 編寫整潔 CSS 代碼的黃金法則,</b>

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

避免使用全局選擇器和元素選擇器

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

使用語義化類名

避免 CSS 和标簽結構的緊耦合

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

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

這段代碼看似無傷大雅,但如果我們需要一個樣式不同的 <code>button</code> 呢?假設需要一個用于關閉對話框元件的 <code>.close</code> button:

現在,需要編寫 CSS 代碼來覆寫那些不需要繼承于 <code>.button</code> 的屬性:

除此之外,還需要編寫大量類似代碼來覆寫浏覽器的預設樣式。但如果将元素選擇器 <code>button</code>用類選擇器 <code>.default</code> 來替代會如何呢?顯而易見,<code>.close</code> 不再需要指定<code>display</code>、<code>font-weight</code>、 <code>line-height</code>、<code>margin</code>、 <code>padding</code>和<code>width</code>等屬性,這便減少了 23% 的代碼量:

還有一點同樣重要:避免使用全局選擇器有助于減少樣式沖突,即某個子產品(或頁面)的樣式不會意外地影響到另一個子產品(或頁面)的樣式。

對于重置和統一浏覽器預設樣式,全局選擇器完全适用;但對于其他大部分情況而言,全局選擇器隻會造成代碼臃腫。

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

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

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

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

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

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

但 <code>&lt;p class="message error"&gt;</code> 卻會得到 <code>.message.error</code> 的背景和 <code>.error</code> 的邊框:

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

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

在一個 HTML 文檔中一個 <code>id</code> 隻能對應一個元素,是以應用于 <code>id</code> 選擇器的 CSS 規則是很難複用的。這樣做一般都會涉及到一系列的 <code>id</code> 選擇器,例如 <code>#sidebar-features</code> 和<code>#sidebar-sports</code>。

此外,<code>id</code> 選擇器具有很高的權重,要想覆寫它們就必須使用更“長”的選擇器。例如下面這段 CSS 代碼,為了覆寫 <code>#sidebar</code> 的背景顔色屬性,必須使用 <code>#sidebar.sports</code> 和<code>#sidebar.local</code>:

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

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

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

在某些情況下,你可能确實需要 <code>id</code> 選擇器的高特殊性。例如,一些媒體站點可能需要其所有子站都使用同樣的導覽列元件,該元件必須在所有站點都表現一緻并且其樣式是難以被覆寫的。此時,使用 <code>id</code> 選擇器就可以減少導覽列樣式被意外覆寫的情況。

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

權重過高的選擇器大多源于預處理器中過多的嵌套(譯注:此處所指應是 Sass 中選擇器嵌套過深)。

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

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

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

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

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

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

在上述情況下,類選擇器再一次表現出其優點。使用 <code>.promo &gt; .headline</code> 或 <code>.media .title</code>(或者更簡單一些: <code>.promo-headline</code> 和 <code>.media-title</code>)使得在改變标簽結構的時候無需改變 CSS 代碼。

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

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

<b></b>

<b>原文釋出時間為:2017年3月23日</b>

<b>本文來自雲栖社群合作夥伴掘金,了解相關資訊可以關注掘金網站。</b>