[譯] 再談 CSS 中的代碼味道
談 CSS 中的代碼味道
回到 2012 年,我寫了一篇關于潛在 CSS 反模式的文章 CSS中的代碼味道 。回看那篇文章,盡管四年過去了,我依然認同里面的全部內容,但是我有一些新的東西加到列表中。再次說明,這些內容并不一定總是壞的東西,因此把它們稱為代碼味道:在你的使用案例中它們也許可以很好的被接受,但是它們仍然讓人覺得有一點奇怪。
在我們開始前,讓我們回想一下什么是代碼味道,摘自 維基百科 (emphasis mine):
代碼味道,也被稱作代碼異味,在計算機編程領域,指程序源代碼中的任何 有可能預示著更深層次問題 的征兆。按照 Martin Fowler 所說的,「代碼味道是一種表面跡象,通常對應著系統中的深層次問題」。另外一種看待代碼味道方式是關于準則和質量:「代碼味道是代碼中某種特定的結構表明了 違反了基本的設計準則 并且對設計質量產生負面影響」,代碼味道通常不是 bug —— 它們不是技術性的錯誤 并且不會當時就對程序的功能產生阻礙。相反的, 它們預示著可能拖慢開發的設計缺陷 或者增大未來出現 bug 或者故障的風險。代碼異味是導致技術債的因素的指示器。Robert C. Martin 將一系列代碼味道稱作軟件技藝的「價值體系」。
因此, 它們并不總是技術上的錯誤, (不過)它們可作為一個不錯的檢驗方法。
<h2>@extend</h2>希望我可以把這第一條講得細致又簡潔:我早就被告知 @extend 的副作用和陷阱,我也會積極地認為它是代碼味道。它也并不絕對的不好,雖然通常是的。對它應該持懷疑態度。
<p>@extend 的問題是多方面的,可以概括如下:</p>-
它對性能的影響事實上比 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;
}
}</code></pre>
編譯成:
.foo {
color: red;
}
.foo-bar {
font-weight: bold;
}</code></pre>
顯而易見的好處是簡潔:事實上我們只用寫一次命名空間 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 {}</code></pre>
…關鍵選擇器是:
- .foo ,
- .bar ,
- a ,
- .btn .
如果我負責一個代碼庫并且 ack for .btn ,我可能看到如下輸出:
.btn {}
.header .btn,
.header .btn:hover {}
.sidebar .btn {}
.modal .btn {}
.page aside .btn {}
nav .btn {}</code></pre>
除了很多普遍存在的相當糟糕的 CSS,我在這里想指出的問題是 .btn 被定義了很多次,這告訴我:
- 沒有遵循 Single Source of Truth 告訴我按鈕看起來是什么樣的;
- 有很多變化 意思是 .btn 類有很多潛在的不同的樣式,所有的這些都是通過 CSS 的可變性造成的。
一看到像這樣的 CSS,我就意識到在按鈕上做任何工作都將會有很大的影響,追蹤按鈕樣式到底來自哪里將會非常困難,并且任何位置的改動都有可能對其他地方造成影響。這就是 CSS 可變性的關鍵性問題之一。
使用 BEM 的命名形式以便創建全新的類名稱以應對這些改變,例如:
.btn {}
.btn--large {}
.btn--primary {}
.btn--ghost {}</code></pre>
每個只有一個關鍵選擇器。
一個類名出現在另一個組件的文件中
在一個和上面相似但是稍微不同的場景里,類名出現在另一個組件的文件中預示著代碼味道。
上一個代碼味道處理同一個關鍵選擇器有多于一個實例的問題,這個代碼味道處理這些選擇器應該放在哪。這個問題來自于 Dave Rupert :
如果我們需要給某些因為它們的上下文的不同而加樣式,我們應該把這些額外的樣式加到哪呢?
- 要加樣式的對象所在的文件里?
- 控制該對象上下文的文件里?
讓我們假設我們有如下 CSS:
.btn {
[styles]
}
.modal .btn {
font-size: 0.75em;
}</code></pre>
.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]
}</code></pre>
而是像這樣:
// _components.buttons.scss
.btn {
[styles]
}
// _components.modal.scss
.modal {
[styles]
}
.modalbtn {
[styles]
}</code></pre>
第三,新的類名稱將會應用于 HTML 上,像這樣
<div class="modal">
<button class="btn modal
btn">Dismiss</button>
</div></code></pre>
這被叫做 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 文件;
- 開始渲染頁面。
所以我們在這里對我先前那篇關于代碼味道的文章做了幾點添加。這些是我已經看到的并且忍受著的幾點:希望現在你也可以避開他們。
來自:https://juejin.im/entry/58c2042cda2f600d8722131b