如何寫一份不錯的 CSS 代碼?
背景介紹:當我們在從事大項目或團隊開發工作時,我們經常會發現我們寫的代碼,凌亂、難以閱讀並且難以擴展。尤其是當一段時候後我們回頭再看自己的代碼,必須回想起當初自己寫的時候的思路才能看懂。
因此,人們嘗試在代碼風格上保持統一,然而,最大的困難是:修改一個較小的問題,都可能創建更多醜陋的 hack,也可能 CSS 的小改變會影響 JavaScript 的功能。但是這些問題能在我們的項目開始的時候精心規劃,就能很大程度上避免這些問題。今天就來討論一下如何寫一份不錯的 CSS 代碼
一個好的 css 代碼是什麼樣的呢
-
保持樣式表可維護
-
保持代碼可讀性
-
保持樣式表的可擴展性
要保持良好的 CSS 代碼,首先需要訂立一致的 CSS 團隊規範,這就必須從 CSS 架構講起。
CSS 架構
目前 CSS 主要有以下五種設計架構
1. OOCSS
面向對象的 CSS,
-
結構和主題分離 - 減少對 HTML 結構的依賴
-
主題和主題分離 - 增加樣式的複用性
在 OOCSS 的基礎上,出現了另一種設計模式
2. BEM
也可以被當成一種命名規範,本質上使頁面結構清晰
塊(Block)、元素(Element__)、修飾符(Modifier--)
有以下幾個規則
-
Block 元素應該以元素本身的屬性爲主
-
Element 則以元素位置和形狀爲主
-
Modifier 則修飾當前的狀態和主題
-
Element 一定是在 Block 之中,而不能獨立於 Block 之外
-
Modifier 則更多的表示當前組件的形狀和狀態
可以明顯發現
-
結構清晰
-
定位迅速
-
功能明確
在面對組件化的場景時,Block 代表邏輯上和功能上獨立的頁面組件。Element 封裝了行爲 (JavaScript)、模板、樣式(CSS) 和其他實現技術。
舉個例子
<header class="header">
<img class="logo">
<form class="search-form">
<input class="input">
<button class="button"></button>
</form>
<ul class="lang-switcher">
<li class="lang-switcher__item">
<a class="lang-switcher__link" href="url">en</a>
</li>
<li class="lang-switcher__item">
<a class="lang-switcher__link--active" href="url">ru</a>
</li>
</ul>
</header>
block-name__element-name--modifier-name--modifier-value
在 React 當中,也採用了這樣的命名方式
BEMnaming 工具 [1],提供 BEM 命名的檢測
然而在面對大型的項目時 CSS 的凌亂也很難讓開發者願意在茫茫多的代碼中尋找可複用的代碼
3. SMACSS
(What’s Smacss)[https://smacss.com/]
設計的主要規範有三點:
-
Categorizing CSS Rules(爲 css 分類)
-
Naming Rules(命名規範)
-
Minimizing the Depth of Applicability(最小化適配深度)
爲了實現清晰的 CSS 結構,將 CSS 分爲
-
Base - 全局樣式,如 global-reset、normalize.css
-
Layout - 頁面佈局,如 grid
-
Module - 組件佈局
-
State - 元素狀態,如 visible、hidden
-
Theme or Skin - 更多是具體主題的配置樣式
-
JavaScript 勾子 (JavaScript hooks)
其中尤其建議 JavaScript 解除和樣式的耦合
<button class="btn btn--buy js-buy-now"></button>
命名規範上出現了一些差異
.layout-header
.is-hidden
.theme-nav
最小化適配深度,減少 html 和 css 的耦合度,避免 html 的變動增加對 css 的影響
.sidebar ul h3 {}
.side {}
4. ITCSS
對 CSS 進行了更加詳細的分層
如果從功能的角度上看呢,是將 Base 分成了 Settings、Tools、Generic 和 Base,而 Objects、Components 和 Trumps 則分別對應 Layout、Module、(State、Theme),而這樣設計的好處在於可以將代碼的複用性進一步提升
5. ACSS
一個樣式屬性一個類,其中的典型代表就是 TailwindCSS[2],缺點則是破壞了語義化
.block{ display: block; }
.hidden { display: none; }
.p-2 { padding: 0.75rem; }
.flex { display: flex; }
.text-base { font-size: 1rem; }
.bg-green-200 { background-color: #123456 }
<div class>I am Ok</div>
而上述的架構思想,更多則是需要團隊成員的一致性認同,才能實現在代碼風格上的統一。
除了這些開發自律性上的代碼規範外,還有什麼其他的方式來提升 CSS 質量呢?
CSS 預處理器
在預處理器中,同樣提供了衆多的方法來簡化與控制 CSS 代碼,以 stylus 爲例
1. 變量
font-size = 14px
body
font font-size Arial, sans-serif
pad(types = padding, n = 5px)
if padding in types
padding n
if margin in types
margin n
body
pad()
body
pad(margin)
body
pad(padding margin, 10px)
// Yields:
body {
padding: 5px;
}
body {
margin: 5px;
}
body {
padding: 10px;
margin: 10px;
}
2. 函數
add(a, b = a)
a + b
add(10, 5)
// => 15
get(hash, key)
return pair[1] if pair[0] == key for pair in hash
hash = (one 1) (two 2) (three 3)
get(hash, two)
// => 2
get(hash, three)
// => 3
get(hash, something)
// => null
3. 內建函數
// 提取顏色分量
red(#c00)
// => 204
red(#000, 255)
// => #f00
4. 插值
// 屬性插值
vendor(prop, args)
-webkit-{prop} args
-moz-{prop} args
{prop} args
border-radius()
vendor('border-radius', arguments)
box-shadow()
vendor('box-shadow', arguments)
button
border-radius 1px 2px / 3px 4px
// Yields:
button {
-webkit-border-radius: 1px 2px / 3px 4px;
-moz-border-radius: 1px 2px / 3px 4px;
border-radius: 1px 2px / 3px 4px;
}
// 選擇器插值
table
for row in 1 2 3 4 5
tr:nth-child({row})
height: 10px * row
// Yields:
table tr:nth-child(1) {
height: 10px;
}
table tr:nth-child(2) {
height: 20px;
}
table tr:nth-child(3) {
height: 30px;
}
table tr:nth-child(4) {
height: 40px;
}
table tr:nth-child(5) {
height: 50px;
}
mySelectors = '#foo,#bar,.baz'
{mySelectors}
background: #000
Yields:
#foo,
#bar,
.baz {
background: #000;
}
// 對象插值
foo = {
width: 10px,
height: 20px,
'&:hover': {
padding: 0
}
}
.bar
{foo}
Yields:
// => .bar {
// width: 10px;
// height: 20px;
// }
// .bar:hover {
// padding: 0;
// }
5. @EXTEND
form
input[type=text]
padding: 5px
border: 1px solid #eee
color: #ddd
textarea
@extends form input[type=text]
padding: 10px
//Yielding:
form input[type=text],
textarea {
padding: 5px;
border: 1px solid #eee;
color: #ddd;
}
textarea {
padding: 10px;
}
對於維護一份高質量的 CSS 代碼,註釋和間隔必不可少
以下是一種比較建議的註釋和間隔方式,可以自行取用。
/*------------------------------------*\
#A-SECTION
*------------------------------------*/
.selector { }
/*------------------------------------*\
#ANOTHER-SECTION
*------------------------------------*/
/**
* Comment
*/
.another-selector { }
除了縮進,我們還可以通過在規則集之間自由而明智地使用空格來提供大量信息。我們用:
-
密切相關的規則集之間的一 (1) 條空行。
-
鬆散相關的規則集之間的兩 (2) 條空行。
-
全新部分之間的五 (5) 行空行。
// good case
/*------------------------------------*\
#FOO
*------------------------------------*/
.foo { }
.foo__bar { }
.foo--baz { }
// bad case
.foo { }
.foo__bar { }
.foo--baz { }
同理,在 html 結構中,也可以使用同樣的規則。
除了以上這些,還有衆多的規範和優化可以繼續探索,如選擇器性能,CSS 嵌套,有興趣的讀者可以繼續探索
你會認爲 CSS 規範是一個有點宏大和不必要的概念:爲什麼這麼簡單、這麼直接的東西需要像架構一樣被設計成非常複雜的東西?!
正是因爲 CSS 的簡單性、鬆散性和不守規矩的性質意味着在任何合理規模上管理(閱讀、馴服)它的最佳方式是通過嚴格和特定的架構。堅實的架構可以幫助我們控制我們的特殊性,強制執行命名約定,管理我們的源代碼順序,創建一個健全的開發環境,並且通常使我們的 CSS 項目管理更加一致和舒適。
總的來說,可以依照一下幾個規則訂立團隊 / 個人代碼規範,保證代碼的一致性
建議的幾個原則
單一職責原則: 每個 CSS 實現都必須有一個單一的責任。
Correct:
.button {
font-family: Arial, sans-serif;
border: 1px solid black;
background: #fff;
}
.header__button {
margin: 30px;
position: relative;
}
Incorrect:
.header__button {
font-family: Arial, sans-serif;
position: relative;
border: 1px solid black;
margin: 30px;
}
開閉原則: 元素應該通過修飾符擴展,而不是直接在原有基礎上修改。
Original:
<button class="button">...</button>
.button {
font-family: Arial, sans-serif;
text-align: center;
font-size: 11px;
line-height: 20px;
}
Extend
<button class="button button_size_s">...</button>
.button {
font-family: Arial, sans-serif;
text-align: center;
font-size: 11px;
line-height: 20px;
}
.button_size_s {
font-size: 13px;
line-height: 24px;
}
DRY 原則:將有意義的重複規範化和抽象化
巧用mixin和extend
@mixin my-web-font() {
font-family: "My Web Font", sans-serif;
font-weight: bold;
}
.btn {
display: inline-block;
padding: 1em 2em;
@include my-web-font();
}
.foo {
color: red;
}
.bar {
@extend .foo;
}
組合優於繼承和關注點分離
// 將寫js的方式同樣適用在css上
<div class="layout">
<div class="layout__item two-thirds">
<section class="content">
...
</section>
</div>
<div class="layout__item one-third">
<section class="sub-content">
...
</section>
</div>
</div>
參考文獻
BEM 簡介 [3]
OOCSS 介紹 [4]
探索 SMACSS:可擴展的模塊化 CSS 框架 [5]
編寫高效的 CSS 選擇器 [6]
CSS 的單一原則 [7]
思考 CSS 架構 [8]
參考資料
[1]
BEMnaming 工具: https://github.com/bem/bem-sdk#naming
[2]
TailwindCSS: https://www.tailwindcss.cn/
[3]
BEM 簡介: https://en.bem.info/methodology/quick-start/
[4]
OOCSS 介紹: http://oocss.org/
[5]
探索 SMACSS:可擴展的模塊化 CSS 框架: https://zhuanlan.zhihu.com/p/44851489
[6]
編寫高效的 CSS 選擇器: https://csswizardry.com/2011/09/writing-efficient-css-selectors/
[7]
CSS 的單一原則: https://csswizardry.com/2012/04/the-single-responsibility-principle-applied-to-css/
[8]
思考 CSS 架構: https://zhuanlan.zhihu.com/p/32952130
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/XlULcjzJbk9t0QpRIIq1Ag