你是否覺得 CSS 不再跟以前一樣了呢?最近幾年成了熱門話題,許多聰明的人也在談?wù)撍?。CSS 遠(yuǎn)不止是前端開發(fā)者應(yīng)該用來美化網(wǎng)頁的小玩意兒。我們關(guān)心性能,想要創(chuàng)作出更好的網(wǎng)站。
在這篇文章里,我會分享最近幾個月學(xué)習(xí) CSS 相關(guān)的知識和我個人對編寫 CSS 代碼確切的看法。作為程序員,我真的對每樣?xùn)|西的結(jié)構(gòu)部分很感興趣。我覺得編寫 CSS 的方式應(yīng)該改變并對此深入研究。我尋找好的處理方式,最佳準(zhǔn)則(best principles)和新的工作流程(workflows)。
這篇文章就像是在 CSS 世界里旅行的總結(jié)。很多人說編寫 CSS 不是真正的編程。我并不認(rèn)同,它同樣充滿樂趣和挑戰(zhàn)性。
當(dāng)一個程序員開始寫 CSS 時發(fā)生了什么 當(dāng)一個程序員開始寫 CSS 時發(fā)生了什么:你 -> 編寫代碼 -> 預(yù)處理器 -> CSS 代碼 -> 網(wǎng)頁
好吧,讓我們面對它。這世上編寫純的 CSS 不是件有趣的事情。預(yù)處理器使用一些類似 CSS 語法,神奇地生成有效的 CSS 代碼。在你和最終發(fā)送給瀏覽器的樣式之間,它添加了一個中間層。這沒有聽起來那么壞,因為預(yù)處理器提供了一些真正實用的特性。
我認(rèn)為能使你的文件合并在一起是最有價值的事情之一。我確信,你了解當(dāng)在你的 .css
文件用 @import
時,實際上告訴瀏覽器“麻煩你順便也捎帶這個文件”。 確實如此,發(fā)送新的請求,這有點不好,因為你可能會有非常多的文件。發(fā)送額外的請求會降低應(yīng)用的性能。如果你使用 CSS 預(yù)處理器,這個問題將會解決。它們會很容易地把你所有的樣式編譯到單一的 .css
文件。
主要有兩個 CSS 預(yù)處理器 —— LESS 和 SASS。它們都支持?jǐn)U展。沒錯,工作方式略有不同,不過想法(idea)是一樣的。你寫一個有一串屬性的基本類(通常稱作 mixin
),之后把這些屬性導(dǎo)入到另一個選擇器。例如:
// less
.bordered(@color: #000) {
border: dotted 2px @color;
}
.header { .bordered; }
.footer { .bordered(#BADA55); }
// compiles to
.header {
border: dotted 2px #000000;
}
.footer {
border: dotted 2px #bada55;
}
這里有個問題,如果你定義了一個沒有參數(shù)的 mixin,也就是說像這樣:
.bordered {
border: dotted 2px #000;
}
它會原樣編譯到 CSS 文件里,不管你是否使用到。就像這樣,因為這是有效的選擇器。在 SASS 里,我們會多一點點靈活性。分別是 mixins
,extends
和 placeholders
(如果你想了解它們準(zhǔn)確的不同之處,我強烈推薦這篇文章。讓我們看下 SASS 及其編譯后的結(jié)果:
// sass
@mixin bordered($color: #000) {
border: dotted 2px $color;
}
.header { @include bordered; }
.footer { @include bordered(#BADA55); }
// compiles to
.header {
border: dotted 2px black;
}
.footer {
border: dotted 2px #bada55;
}
看起來幾乎和 LESS 相同,但如果我們看下第二個用例,定義一個占位符(a place holder):
// sass
%bordered {
border: dotted 2px #000;
}
.header {
@extend %bordered;
}
.footer {
@extend %bordered;
}
// compiles to
.header, .footer {
border: dotted 2px #000;
}
有兩個很好的事情發(fā)生。首先,這里不會編譯 .bordered
類(there is no .bordered class compiled)。第二,SASS 合并了選擇器,這讓我們的 CSS 更短一些。
LESS 和 SASS 都支持定義變量。你可以稍后再訪問這些變量,使用它們作為屬性的值。
// sass
$brand-color: #009f0A;
...
h1 {
color: $brand-color;
}
這是個好的特性,因為你可能會在同一個地方,存儲一些像公司的顏色或網(wǎng)格寬度之類重要的東西。如果你想要修改,可以不用檢查一邊所有的代碼。
另一個方便的用法是插入變量。下面的例子演示這種方法:
// sass
@mixin border($side) {
border-#{$side}: solid 1px #000;
}
.header {
@include border("left");
}
// compiles to
.header {
border-left: solid 1px #000;
}
預(yù)處理器是一個工具,也就是說,你必須多做一件事,把它添加到把你的開發(fā)環(huán)境中。你可能想要把它整合進(jìn)你的應(yīng)用里,當(dāng)然這需要額外編寫代碼。
如果你不想讓你的代碼跟預(yù)處理器的弄亂,那么你很有可能需要一個監(jiān)聽工具。另一個用來監(jiān)聽你文件的工具,一旦文件有更新就會生成編譯后的版本。如果是這樣的話,那么每次當(dāng)你開始開發(fā)項目的時候都要運行這個監(jiān)聽工具。也許你會優(yōu)化這個過程所需的時間,但它還是需要你多留一份心。
許多開發(fā)者總是只盯著他們的 .less
或者 .sass
文件。但編譯后的文件才是重要的。你的 SASS 代碼可能很優(yōu)雅并優(yōu)化過的,但這并不意味著你最后得到同樣優(yōu)美的 CSS 代碼。你可能會有真正需要關(guān)心的特定問題。因此,定期地檢查編譯后的版本。
BEM 代表塊 Block 元素 Element 修飾符 Modifier BEM代表塊(Block),元素(Element),修飾符(Modifier)。
好吧,我找到一個可以玩的新工具。預(yù)處理器也許能夠節(jié)省大量的時間,但是單獨使用它們不能寫出好的結(jié)構(gòu)。我開始思考的第一件事是命名規(guī)范。讓我們看下面 HTML 代碼:
<header class="site-header">
<div class="logo"></div>
<div class="navigation"></div>
</header>
樣式可能跟這個類似:
.site-header { ... }
.logo { ... }
.navigation { ... }
當(dāng)然,這會奏效。但有個問題,閱讀這個 CSS 你不能理解它,例如, logo
屬于 header
。你也許有另一個小的 logo
圖片用在 footer
。下一個邏輯步驟是寫一個后代選擇器。
.site-header .logo { ... }
然而使用這種選擇器不是個好主意,因為這把樣式緊綁到特定的標(biāo)記層次(it tights styles to specific tags hierarchy)。如果我把 logo
移到 header
標(biāo)簽外面會怎樣呢?這樣式會失效。另一個你能做的是把 site-header
添加到 logo
類的名字中:
.site-header-logo { ... }
這很好,不言自明(self explanatory)。但這不是在所有的情況下都奏效。以后,在十二月份可能想要使用圣誕節(jié)版的 logo。那么,我不能寫成:
.site-header-logo-xmas { ... }
因為我的邏輯是寫一個選擇器能夠匹配嵌套在 HTML 里的標(biāo)記。
BEM 是這種情況的解決方案。它意思是塊(Block),元素(Element),修飾符(Modifier)和創(chuàng)建一些你可以遵循的規(guī)則。使用 BEM,我們小小的例子會變成這樣:
.site-header { ... } /* block */
.site-header__logo { ... } /* element */
.site-header__logo--xmas { ... } /* modifier */
.site-header__navigation { ... } /* element */
也就是說,.site-header
是我們的塊(our block)。logo 和 導(dǎo)航(navigation)這個塊的元素(elements),logo 的 xmas
版本是修飾符(modifier)。也許這看起來簡單,但這真的強大。一旦你開始使用它會發(fā)現(xiàn)讓你更好的結(jié)構(gòu)化。反對的理由主要是 BEM 的語法。沒錯,這看來確實有點難看,但我準(zhǔn)備好對這個好系統(tǒng)的命名妥協(xié)。
面向?qū)ο蟮?CSS 面向?qū)ο蟮?CSS
我找到了 BEM 就能準(zhǔn)確地命名我的類,然后就開始思考構(gòu)造(composition)。也許我第一次閱讀的文章是關(guān)于面向?qū)ο蟮?CSS。面向?qū)ο缶幊逃袝r是關(guān)于添加抽象并且 CSS 能夠支持它。是否使用預(yù)處理器,你都應(yīng)該了解 OOCSS 。作為碼農(nóng)(coder),我發(fā)現(xiàn)這個理念真的跟平時編程很接近,例如 JavaScript 。這是兩個主要原則(principles):
讓我們用下面的例子:
.header {
background: #BADA55;
color: #000;
width: 960px;
margin: 0 auto;
}
.footer {
background: #BADA55;
text-align: center;
color: #000;
padding-top: 20px;
}
這里有些樣式是重復(fù)的。我們可以把它們提取到另一個類,如下:
.colors-skin {
background: #BADA55;
color: #000;
}
.header {
width: 960px;
margin: 0 auto;
}
.footer {
text-align: center;
padding-top: 20px;
}
這樣,我們就有了可擴(kuò)展的 colors-skin
對象。HTML 代碼也許跟下面的類似:
<div class="header colors-skin"> ... </div>
<div class="colors-skin"> ... </div>
<div class="footer colors-skin"> ... </div>
這種改變有幾個好處:
這里的理念是,不管每個元素放在哪里,都應(yīng)該被應(yīng)用同一種樣式。因此,你應(yīng)該避免跟下面類似的選擇器用法:
.header .social-widget {
width: 250px;
}
這是因為如果你把 .social-widget
移到 .header
容器(container)外面,寬度(width)就會不相同。一般來說這不是好的做法,尤其是給整個頁面到處都使用的部分添加樣式。這個原則鼓勵使 CSS 模塊化,我強烈建議利用充足的時間嘗試下。就我個人而言,遵守這個原則意味著能夠編寫出更好的 CSS。
如果你在 GitHub 打開 OOCSS repository 會看到一個框架。是的,這個框架用到面向?qū)ο蟮?CSS 理念,并且有一堆很酷的組件可以立即使用(ready-to-use)。某些時候我并不喜歡框架。如果你思考一下的話會發(fā)現(xiàn),framework 這個單詞有部分 frame 和 work,你確實在條條框框(frame)里工作(work)。你確實要對框架妥協(xié)并且必須遵守它的規(guī)則。我更愿意使用微框架(micro-frameworks)或者只提供最基本特性的工具。當(dāng)然我不是打算重新發(fā)明輪子,但我總是試著在兩者之間取得平衡。經(jīng)常這樣,現(xiàn)成可用(ready-to-use)的解決方案會帶來混亂復(fù)雜的系統(tǒng)。我的建議是,干一件事只為了一個特定的目的。如果你可能多地包含方方面面,你的下場將會是……你懂的,一個框架。
但是,我強烈推薦你查看下 OOCSS 框架。這是一個獨特的知識(It’s an unique piece of knowledge),也許會滿足你的需求。Nicole Sullivan 托管這個 repository (倉庫)。她是 OOCSS 的創(chuàng)始人(She is a pioneer in OOCSS),如果你有空的話我建議你查看下這個 presentations/talks。
可量化和模塊化 CSS 架構(gòu) Scalable and Modular Architecture for CSS 可量化和模塊化的 CSS 架構(gòu)
另一個流行的理念是 SMACSS。SMACSS 表示可量化(Scalable)和模塊化(Modular)的 CSS 架構(gòu)。Jonathan Snook 為 CSS 開發(fā)者介紹了風(fēng)格指南(introduces something like style guide for the CSS developers)。這個想法是把你的應(yīng)用分成以下幾種類型:
原子設(shè)計 原子(Atoms) -> 分子(molecules) -> 組織(organisms) -> 模板(Templates) -> 網(wǎng)頁(Pages)
了解 OOCSS 和 SMACSS 之后我尋找一個恰當(dāng)?shù)南笳鳎╩etaphor),很快我看到了 Atomic Design 這篇文章,很好地展示 原子設(shè)計( Atomic Design) 這個好的理念。作者是 Brad Frost,是個知名的 Web 開發(fā)者,主要從事自適應(yīng)式(responsive)和移動終端方面的工作。
這個理念真的很有趣。效仿一些化學(xué)技術(shù),物質(zhì)的基本單位是原子。Brad 把這移到 CSS,我們的網(wǎng)頁是由原子構(gòu)成的。一個原子是這樣的:
<label>Search the site</label>
或者
<input type="text" placeholder="enter keyword" />
也就是說,原子包含 DOM 元素一些基本的樣式。例如調(diào)配顏色(color palette),字體大小或者轉(zhuǎn)換(transitions)。稍后這些部分會合并成一個分子(molecules)。例如:
<form>
<label>Search the site</label>
<input type="text" placeholder="enter keyword" />
<input type="submit" value="search" />
</form>
這樣 form
元素包含了幾個原子。像這樣的抽象化帶來靈活性,因為我們也許會用同樣的原子構(gòu)建另一個分子。這樣(Together with that),我們能在不同的地方(contexts)重復(fù)使用同樣的 form
。
Brad 并沒有止步于此。分子構(gòu)成了組織(organisms)。按照同樣的方法,我們可以寫成如下,把它稱作組織(organisms):
<header>
<div class="logo">
<nav>
<ul>
<li><a href="#">Home</a></li>
<li><a href="#">About</a></li>
<li><a href="#">Contacts</a></li>
</ul>
</nav>
<form>
<label>Search the site</label>
<input type="text" placeholder="enter keyword" />
<input type="submit" value="search" />
</form>
</header>
這個理念的下一個是模板(templates)。這沒有跟化學(xué)直接相關(guān),而是放到 Web 環(huán)境中(web context)。一旦我們開始合并不同的組織,就是在構(gòu)造模板。之后這些模板形成了最終的網(wǎng)頁。
你可能已經(jīng)使用相似的方法開發(fā)你的應(yīng)用軟件。然而,以合理的方式命名會帶來好的架構(gòu)。你和所有你的團(tuán)隊隊員在開發(fā)中會明白一些事情。把一件東西分成原子和分子是挺重要的一部分,因為這會改善 Web 應(yīng)用程序的開發(fā)過程和維護(hù)。
原子設(shè)計 有機(jī)的 CSS Organic CSS
幾個月前我寫了一篇關(guān)于 Organic 的文章。這是一個很小的 JavaScript 應(yīng)用程序框架。它更像是設(shè)計模式,我個人很喜歡它。我甚至在幾個項目里使用了 Organic 并且它干得非常好。如果你對它感興趣,我強烈建議你閱讀這篇博文。
當(dāng)我看 Brad Frost 的文章時,我已經(jīng)對相似的理念非常熟悉,因為我了解 Organic 。Brad 的 Atomic Design 十分出色,但我決定更進(jìn)一步,基于原子設(shè)計(Atomic Design)理念試著寫自己的微框架。我選擇 SASS 作為預(yù)處理器并在 Github 創(chuàng)建了倉庫
讓我們從框架最小的部分開始 —— 原子。在維基百科的定義是 *原子是一種元素能保持其化學(xué)性質(zhì)的最小單位。*。在 CSS 的環(huán)境(context)中,我認(rèn)為是一個屬性和它的值。例如:
margin-top: 24px;
添加原子只是直接在類里面寫樣式,這種方式不是我想要的。因此如果寫成下面這樣:
body {
margin-top: 24px;
}
header {
margin-top: 24px;
}
預(yù)處理器就會保持這個樣子。我想要的最后結(jié)果是:
body, header {
margin-top: 24px;
}
SASS 使用占位符(place holders)實現(xiàn)這個效果。也就是說:
%margin-top-24 {
margin-top: 24px;
}
body {
@extend %margin-top-24;
}
header {
@extend %margin-top-24;
}
因此,我必須使用占位符(placeholders)。這也就意味著我必須有大量預(yù)定義的并且我能使用的占位符(placeholders)。就在那時,我決定這個框架將只包含原子。也許還有一些類似通常的 reset.css
,網(wǎng)格定義等等。我想要為 CSS 開發(fā)寫一些基本的東西(I wanted to write something which acts as a base for the CSS development)。也許一個又一個項目之后我將會看到一些可以放進(jìn)核心的模式,但剛開始我想保持 repo(倉庫)整潔簡單。為了使一些東西始終如一的,我為定義原子創(chuàng)建 mixin。如下:
@include define-atom("block") {
display: block;
}
@include define-atom("font-family") {
font-family: Georgia;
}
使用這種方式我創(chuàng)建了一堆容易應(yīng)用到每個項目的原子。你可以在這里查閱。我從別的框架里使用了一些最佳做法,所以并不是全歸功于我。這里是個在分子里混合原子的 mixin:
@mixin header { /* <- molecule called 'header' 稱作 `header` 的分子*/
@include atoms((
block,
clearfix,
font-family
));
}
分子是需要樣式的 DOM 元素,但是沒有子代?;蛘呤菦]有直接聯(lián)系的子代。例如 <img src="logo.jpg" />
可以是分子。如果你覺得在網(wǎng)頁里識別出分子,只需思考下什么是有原子構(gòu)成的。如果一些元素是由其他分子構(gòu)成的,那么很可能是一個(細(xì)胞器)。下面幾行展示的是如何定義一個分子:
@mixin login-box {
@include atoms((
block,
font-size-20,
margin-top-23,
bold
));
}
我面前有些有趣的東西。讓我們看下 body
標(biāo)記。這是什么?是一個分子或者其他東西嗎?沒錯,它通過原子需要一些樣式,但通常包含了其他分子。它應(yīng)該是其他東西。我做出決定,CSS 應(yīng)該是主角。也就是說,如果 body
標(biāo)記需要一些原子提供樣式,那就是分子,理論上,我不該給它附加上任何其他的分子。這看起來有點不切實際,但在大多數(shù)情況下,這會阻止你使用后代選擇器,這個好兆頭。
一旦你能夠識別出哪個 DOM 元素是分子,你將會知道什么細(xì)胞器。例如,典型的 form
元素是一個細(xì)胞器的好例子。它包含了分子像 label
,input
或者 textarea
。
.login-form {
@include label;
@include input;
@include textarea;
}
這個框架中,細(xì)胞器(Organelles)是第一個跟當(dāng)前應(yīng)用緊密相關(guān)的 。原子和分子在不同項目中會有所改變,而細(xì)胞器卻不會。
你也許經(jīng)常想要在其他地方合并幾個細(xì)胞器。如果是這種情況,添加其他抽象:
Atom → Molecule → Organelle → Cell → Tissue → Organ → Sys → Organism
你選擇什么來讓你的 CSS 結(jié)構(gòu)化,這是個問題。目前為止,我只在一個項目里使用過 OrganicCSS,但我能說,它使項目變清晰了。我把不同的元素放在它們各自的目錄(文件夾)里并像這樣命名類,例如,分子在一個“molecules”目錄(文件夾),里面的文件命名為“header_molecule.scss”我才能容易地找到正在編寫的是哪一個。例如,如果有一個叫 header
的細(xì)胞器,我簡單地改成 o-header
。然后,當(dāng)我看 HTML 代碼時可以是了解到,這個元素的 CSS 樣式文件在 organelles
文件夾。
這個一個有趣的旅行。我不知道是否會在將來使用 OrganicCSS,但這不是重點。我學(xué)到的東西才是重要的。我知道必須改變我的 CSS 開發(fā)過程,我做到了。我確信我們應(yīng)該談?wù)摳?CSS 的結(jié)構(gòu)。如你所見,在文章里我們有很多好的資源。我們只是必須找到它們,學(xué)習(xí)它們干了什么和如何運行的。只有我們能決定是否使用它們。更好的是,當(dāng)你了解了整個項目才能創(chuàng)作出能更好滿足需求的東西。
更多建議: