在過去的很長一段時間中,我們都說 CSS 是不帶有任何邏輯的,意思是在 CSS 中沒有控制流,也沒有某種類似於其他編程語言的方式來組織 CSS。CSS 天生缺乏邏輯性的問題導致了預處理器的出現。然而業界卻對 CSS 預處理器褒貶不一,支持預處理器的人認為這彌補了 CSS 缺失的特性;而反對預處理器的人則認為 CSS 的設計初衷就不應該帶有邏輯性,他們認為根本不應該引入預處理器這個概念。
然而,一種獨特的思考方法最近突然蹦入了我的腦袋。它讓我感到 CSS 確實擁有邏輯性!很少有人真正那麼想過,這大概也是我們一直認為 CSS 的邏輯性匮乏的最大原因吧。
我發現我們可以將復合選擇器理解為:主體部分 + 條件部分。首先來看一個例子:
CSS Code復制內容到剪貼板在這個復合選擇器由主體部分是 span,而條件部分是 IF (inside .btn) AND IF (on a) AND IF (inside .login-box) AND IF (inside .sidebar) AND IF (on div)。
也就是說,一個選擇器的每一部分都是一個 if 語句,需要在解析選擇器時被滿足(或者不滿足)。有了這種微妙的而又全新的認識,如今我們回頭再看看自己曾經寫出的 CSS 代碼,我們將會意識到選擇器寫的好或者壞,會對效率產生直接的影響。我們真的會寫出下面這段邏輯嗎?(偽代碼):
CSS Code復制內容到剪貼板也許不會。這看上去太不直接,也太啰嗦了。我們也許只需要這麼寫:
CSS Code復制內容到剪貼板每當為選擇器添加一層限制,其實我們也就是添加了額外的一個 if 語句。這會導致圈復雜度問題(Cyclomatic Complexity)。
圈復雜度
在軟件工程中,圈復雜度是一種程序復雜性的一種度量標准,它一般計算程序中的控制流的數量(如 if, else, while 等)。程序中存在越多的控制流,則圈復雜度就越高。我們自然想要保證圈復雜度能夠盡量地低,因為圈復雜度越高:
代碼就越難推導
更多潛藏著的、可能會導致失敗的問題
代碼更難以修改、維護以及復用
你需要考慮更多代碼執行的結果與其副作用
編寫測試代碼的難度也會更高
從圈復雜度的角度來思考 CSS 的解析過程,我們可以看到浏覽器在渲染樣式之前需要做許多的決定。我們寫的選擇器中的 if 語句越多,這個選擇器的圈復雜度就越高,這也意味著我們寫的選擇器越糟糕,為了使得這一條選擇器規則滿足,就有需要匹配更多的條件。同時,我們寫的選擇器也會缺乏清晰度和復用性,因為引入了過多不必要的 if 語句會導致不准確的匹配(false positive)。
相比於將 span 嵌套於 .btn 內部並寫一大堆限制條件,更好地做法應該是創建一個新的類 .btn-text 來描述這個 span。這樣做更加直截了當,同時也更為簡潔和健壯(越多的 @if 語句導致選擇器規則越不容易被滿足)。
值得注意的是浏覽器解析你寫的選擇器的方式:從右向左。如果你在寫你的選擇器時,第一個想到的問題是:“這是一個 span 元素嗎?” 那你通常就會把選擇器寫的過於冗繁。你應該從另一個角度思考,寫出清晰准確的選擇器規則,徹底摒棄那些冗余的條件語句。
請不要寫過於寬泛的規則,導致你寫的選擇器在匹配開始時就選中大量的 DOM 元素——然後不得不逐步通過更多的條件語句來刪減匹配的對象。從選擇器的規則解析的一開始就匹配盡量少的元素才是一種更棒的方法。
圈復雜度對於 CSS 來說可能是一種比較高階的原則,但如果我們通過它來考量那些蘊含在我們寫的選擇器中的邏輯性,那我們也許就能寫出更加優秀的代碼。
一些易於遵守的小規則,
讓你的選擇器最簡化:每一次你想要為選擇器添加規則時,你都在添加額外的 if 語句。將這些 if 語句大聲地讀出來,仔細考慮它們是否有添加的必要。你需要時刻保持你寫的選擇器足夠合理與簡潔。
保證圈復雜度最小化: 使用像 Parker 這樣的工具來測試你寫的選擇器的圈復雜度(參考文檔:IdentifIErs Per Selector)
如果你不需要這個檢驗條件,那就不要把它放進選擇器: 有時在 CSS 中使用嵌套結構是有必要的,可在大多數時候並不是,你甚至不能完全相信Inception Rule。
從右邊考慮選擇器如何編寫: 從需要匹配的那類元素開始,寫盡量少的額外的 CSS 代碼來完成一次正確的匹配。
寫選擇器時擁有明確的目的性: 確保你寫的選擇器確實是你想要的,而不是那些碰巧能使得頁面正常顯示的代碼。
你的選擇器是你的 CSS 結構最基本的組成部分,一定要確保你寫的代碼足夠合理而簡練。