- 原文地址:Code Smells in CSS Revisited
- 原文作者:Harry
- 譯文出自:掘金翻譯計劃
- 譯者:IridescentMia
- 校對者:rccoder, Germxu
再談 CSS 中的程式碼味道
回到 2012 年,我寫了一篇關於潛在 CSS 反模式的文章 CSS中的程式碼味道。回看那篇文章,儘管四年過去了,我依然認同裡面的全部內容,但是我有一些新的東西加到列表中。再次說明,這些內容並不一定總是壞的東西,因此把它們稱為程式碼味道:在你的使用案例中它們也許可以很好的被接受,但是它們仍然讓人覺得有一點奇怪。
在我們開始前,讓我們回想一下什麼是程式碼味道,摘自 維基百科 (emphasis mine):
程式碼味道,也被稱作程式碼異味,在計算機程式設計領域,指程式原始碼中的任何 有可能預示著更深層次問題 的徵兆。按照 Martin Fowler 所說的,「程式碼味道是一種表面跡象,通常對應著系統中的深層次問題」。另外一種看待程式碼味道方式是關於準則和質量:「程式碼味道是程式碼中某種特定的結構表明了 違反了基本的設計準則 並且對設計質量產生負面影響」,程式碼味道通常不是 bug —— 它們不是技術性的錯誤 並且不會當時就對程式的功能產生阻礙。相反的,它們預示著可能拖慢開發的設計缺陷 或者增大未來出現 bug 或者故障的風險。程式碼異味是導致技術債的因素的指示器。Robert C. Martin 將一系列程式碼味道稱作軟體技藝的「價值體系」。
因此, 它們並不總是技術上的錯誤, (不過)它們可作為一個不錯的檢驗方法。
@extend
希望我可以把這第一條講得細緻又簡潔:我早就被告知 @extend
的副作用和陷阱,我也會積極地認為它是程式碼味道。它也並不絕對的不好,雖然通常是的。對它應該持懷疑態度。
@extend
的問題是多方面的,可以概括如下:
它對效能的影響事實上比 mixins 更嚴重。 Gzip 偏愛重複性的內容,所以具有更高重複性 CSS 檔案 (如 mixins) 取得更高的壓縮量。
它是貪婪的。 Sass 的
@extend
將會@extend
它找到的每個 class 的例項,返回給我們一個相當長的選擇器鏈 看起來像這樣。它移動你的程式碼庫的順序。 在 CSS 中原始的順序至關重要,所以應該總是避免在你的專案中移動選擇器的位置。
它使檔案晦澀難懂。
@extend
在你的 Sass 中隱藏了很多複雜的東西,你需要逐步的拆開,然而在你審閱檔案的過程中,這個複雜的 class 方法將所有的資訊置於焦點。
擴充套件閱讀:
- Mixins Better for Performance
- When to Use
@extend
; When to Use a Mixin - Extending Silent Classes in Sass
為類使用連線字串
另外一個 Sass 讓人惱火的地方就是在你的類上使用 &
連線字串,例如:
.foo {
color: red;
&-bar {
font-weight: bold;
}
}複製程式碼
編譯成:
.foo {
color: red;
}
.foo-bar {
font-weight: bold;
}複製程式碼
顯而易見的好處是簡潔:事實上我們只用寫一次名稱空間 foo
確實是很 DRY (Don't repeat yourself)。
一個不那麼明顯的缺點是,字串 foo-bar
現在在原始碼中不存在。搜尋程式碼庫查詢 foo-bar
只會返回 HTML 中的結果(或者是編譯過的 CSS 檔案,如果你已經把它納入到你的專案中)。想要在原始碼中定位 .foo-bar
的樣式變得非常困難。
我不僅僅是 CSS 全稱寫法的愛好者:總的來說,相比於重新為元素命名一個類,我更喜歡查詢到它原有的類名,所以可查詢性對我來說很重要。如果我加入一個專案大量使用 Sass 的字串連線,追蹤查詢通常都會是非常艱難的。
當然你也可以說 sourcemaps 將會幫助我們,或者如果我正在查詢 .nav__item
這個類,我可以簡單的開啟 nav.scss
這個檔案,但是不幸的是這並不總是奏效。獲得更多的資訊,可以看我做的關於它的 錄屏。
Background 簡寫
我最近討論的另外一個主題就是使用 background
簡寫語法。想了解更多細節,請參考 the relevant article,但是在這裡做一個總結如下:
.btn {
background: #f43059;
}複製程式碼
…當你可能想要表達的意思是:
.btn {
background-color: #f43059;
}複製程式碼
…這是另一種程式碼味道的實踐。當我看到前者被使用的時候,很少是開發者實際上想要的:幾乎任何時候他們真正的意思是後者。後者 僅僅 設定或者改變背景色,而前者將會也重置或者復原背景圖、背景位置、背景連結等。
在 CSS 專案中看到這樣的形式立即提醒我,我們終究會因為它遇到問題。
關鍵選擇器多次出現
關鍵選擇器是獲得目標或者是被賦予樣式的選擇器。它通常在左花括號 ({
) 前面的內容,但也並不總是。在下面的 CSS 中:
.foo {}
nav li .bar {}
.promo a,
.promo .btn {}複製程式碼
…關鍵選擇器是:
.foo
,.bar
,a
,.btn
.
如果我負責一個程式碼庫並且 ack for .btn
,我可能看到如下輸出:
.btn {}
.header .btn,
.header .btn:hover {}
.sidebar .btn {}
.modal .btn {}
.page aside .btn {}
nav .btn {}複製程式碼
除了很多普遍存在的相當糟糕的 CSS,我在這裡想指出的問題是 .btn
被定義了很多次,這告訴我:
- 沒有遵循 Single Source of Truth 告訴我按鈕看起來是什麼樣的;
- 有很多變化 意思是
.btn
類有很多潛在的不同的樣式,所有的這些都是通過 CSS 的可變性造成的。
一看到像這樣的 CSS,我就意識到在按鈕上做任何工作都將會有很大的影響,追蹤按鈕樣式到底來自哪裡將會非常困難,並且任何位置的改動都有可能對其他地方造成影響。這就是 CSS 可變性的關鍵性問題之一。
使用 BEM 的命名形式以便建立全新的類名稱以應對這些改變,例如:
.btn {}
.btn--large {}
.btn--primary {}
.btn--ghost {}複製程式碼
每個只有一個關鍵選擇器。
一個類名出現在另一個元件的檔案中
在一個和上面相似但是稍微不同的場景裡,類名出現在另一個元件的檔案中預示著程式碼味道。
上一個程式碼味道處理同一個關鍵選擇器有多於一個例項的問題,這個程式碼味道處理這些選擇器應該放在哪。這個問題來自於 Dave Rupert:
如果我們需要給某些因為它們的上下文的不同而加樣式,我們應該把這些額外的樣式加到哪呢?
- 要加樣式的物件所在的檔案裡?
- 控制該物件上下文的檔案裡?
讓我們假設我們有如下 CSS:
.btn {
[styles]
}
.modal .btn {
font-size: 0.75em;
}複製程式碼
.modal .btn {}
應該放在哪?
它應該 在 .btn
所在的檔案中。
我們應該儘量將我們的樣式基於主題(例如:關鍵選擇器)分組。在這個例子中,主題是 .btn
:這才是我們真正關心的。.modal
只不過是 .btn
的上下文,所以我們根本沒給它新增樣式。為此,我們不應該將 .btn
的樣式移出到另外的檔案中。
我們不這樣做簡單的因為它們是並列的:將所有按鈕的上下文放在一處更方便。如果我想得到專案中所有按鈕樣式的概觀,我僅僅需要開啟 _components.buttons.scss
,而不是一堆其他的檔案。
這樣做使得將所有按鈕的樣式移入另外一個新專案變得更容易,更重要的是這樣做提前讀懂變得容易。我相信你們都對這種感覺相當熟悉,就是文字編輯器中開啟十餘個檔案,而僅僅試圖修改很小的一處樣式。這是我們能夠避免的。
將你的樣式基於主題的分組到檔案中:如果是給按鈕的樣式,無論它是什麼樣的,我們應該讓它在 _components.buttons.scss
檔案中。
一個簡單的經驗法則就是,問問你自己這樣的問題,我是在給 x 新增樣式還是 y?如果答案是 x,那麼你的 CSS 應該在 x.css
檔案中;如果答案是 y,它應該在 y.css
中。
BEM Mixes
事實上很有趣的,我根本不會這樣寫 CSS —— 我使用 BEM mix —— 但是這是另一個不同問題的答案。不是像下面這樣:
// _components.buttons.scss
.btn {
[styles]
}
.modal .btn {
[styles]
}
// _components.modal.scss
.modal {
[styles]
}複製程式碼
而是像這樣:
// _components.buttons.scss
.btn {
[styles]
}
// _components.modal.scss
.modal {
[styles]
}
.modal__btn {
[styles]
}複製程式碼
第三,新的類名稱將會應用於 HTML 上,像這樣
<div class="modal">
<button class="btn modal__btn">Dismiss</button>
</div>複製程式碼
這被叫做 BEM mix,我們介紹第三種新的類名稱來指向屬於 modal 的按鈕。這樣避免了它在哪裡的問題,它通過避免巢狀,減少了名稱唯一性的問題,同時通過重複 .btn
類避免可變性帶來的問題。完美!
CSS @import
我會說 CSS @import
不僅僅是程式碼味道,它的的確確是壞的實踐。它推遲 CSS 檔案的載入(效能的決定性因素),比實際的需要載入的更晚,造成嚴重的效能下降。下載具有 @import
的 CSS 檔案的(簡化的)工作流程看起來有點像:
- 獲取 HTML 檔案,這個 HTML 檔案中請求 CSS 檔案;
- 獲取 CSS 檔案,這個 CSS 檔案請求另外一個 CSS 檔案;
- 獲取最後一個 CSS 檔案;
- 開始渲染頁面。
如果我們得到 @import
的內容,將其壓入一個單獨的檔案,工作流程看起來將會是這樣:
- 獲取 HTML 檔案,這個 HTML 檔案中請求 CSS 檔案;
- 獲取 CSS 檔案;
- 開始渲染頁面。
如果我們不能將所有的 CSS 放入一個檔案(例如我們連結了谷歌字型),那麼我們應該在 HTML 中使用兩個 <link />
元素,而不是使用 @import
。這可能讓人感覺有點不那麼壓縮(但也是更好的方式處理所有 CSS 檔案的依賴),它對於效能仍然是比較友好的:
- 獲取 HTML 檔案,這個 HTML 檔案中請求 CSS 檔案;
- 獲取所有的 CSS 檔案;
- 開始渲染頁面。
所以我們在這裡對我先前那篇關於程式碼味道的文章做了幾點新增。這些是我已經看到的並且忍受著的幾點:希望現在你也可以避開他們。