CSS架構目標:預測、重用、擴展、維護

對於想踏入前端開發的工程師來講,通曉CSS(Cascading Style Sheets)則是最基本的要求。而擅長CSS的Web開發人員不只能夠從視覺上覆制實物原型,還能夠用代碼進行完美的呈現。無需使用表格、儘量少的使用圖片。若是你是個名副其實的高手,你能夠快速把最新和最偉大的技術應用到你的項目中,好比媒體查詢、過渡、濾鏡、轉換等。雖然這些都是一個真正的CSS高手所具有的,但CSS不多被人單獨拿出來討論,或者用它去評估某我的的技能。css

有趣的是,咱們不多這樣去評價其餘語言。Rails開發人員並不會由於其代碼比較規範,就認爲他是一名優秀的開發人員。這僅僅是個基準。固然,他的代碼得必須規範。另外,還需集合其餘方面考慮,好比代碼是否可讀?是否容易修改或擴展……html

這都是些很天然的問題,CSS和它們並無什麼不一樣之處。今天的Web應用程序要比以往更加龐大。一個缺少深思熟慮的CSS架構每每會削弱發展,是時候像評估其餘語言那樣,來評估一下CSS架構了,這些都不該該放在「過後」考慮或者單單屬於設計師們的事情。前端

1.良好的CSS架構目標程序員

在CSS社區,很難提出某個最佳實踐已經成爲你們的廣泛共識。純粹地從Hacker News的評論上判斷和開發者們對CSS Lint發佈後的反應來看,大多數人對基本的CSS東西是持反對意見的。因此,並非爲本身的最佳實踐奠基一套基本的論據,而應該肯定真正的目標。編程

好的CSS架構目標並不一樣於開發一個好的應用程序,它必須是可預測、可重用、可維護和可伸縮的。api

可預測sass

可預測意味着能夠像預期的那樣規範本身的行爲。當你添加或者修改某個規則時,它並不會影響到沒有指定的部分。對於一個小網站來講,一些微乎其微的改變並不算什麼。而對於擁有成千上萬個頁面的大網站來講,可預測倒是必須的。安全

可重用架構

CSS規則應具有抽象和解耦性,這樣你就能夠在現有的基礎上快速構建新的組件,無需從新修改編碼模式。app

可維護

當把新組件放置到網站上,而且執行添加、修改或者從新設計操做時,無需重構現有CSS,而且新添加的X並不會打破原有頁面的Y組件。

可擴展

當網站發展到必定規模後,都須要進行維護和擴展。可擴展的CSS意味着網站的CSS架構能夠由我的或者團隊輕易地管理,無需花費太多的學習成本。

2.常見的錯誤實踐

在實現良好的CSS架構目標以前,咱們來看一些常見的錯誤作法,這對咱們達成目標是有好處的。

下面的這些例子雖然均可以很好的執行,但卻會給你帶來不少煩惱,儘管咱們的意圖和願望都是美好的,可是這些開發模式會讓你頭疼。

幾乎在每一個網站上,都會有一個特定的虛擬元素看起來與其餘頁面是徹底同樣的,然而只有一個頁面除外。當面對這樣一種狀況時,幾乎每一個新手CSS開發人員(甚至是經驗豐富的)都會以一樣的方式來修改。你應該爲該頁面找出些不同凡響之處(或者本身建立),而後再寫一個新規則去操做。

基於父組件來修改組件

.widget {    background: yellow;    border: 1px solid black;    color: black;    width: 50%;  }   #sidebar .widget {    width: 200px;  }   body.homepage .widget {    background: white;  }

初看,這絕對是段無害的代碼,但讓咱們來看看它是否達到了咱們所設置的目標。

首先,widget在examle是不可預見的。當這些小部件出如今頁面兩側或者主頁面時,開發人員指望它們以某種特定的方式顯示出來,且又不失特點。另外,它也是不可重用或不可擴展的。

另外,它也比較難維護。一旦這個widget須要從新設計,那麼你不得不修改其餘幾個CSS樣式。想象一下,若是這段代碼是使用其餘語言編寫的,它基本就是一個類定義,而後在代碼的另外一部分使用該類定義並作出擴展。這直接違反了軟件開發的開放/閉合(open/close)原則。

軟件實體(類,模塊,函數等)應對擴展開放,對修改閉合。

過於複雜的選擇器

偶爾,會有些文章介紹CSS選擇器對整個網站的展現起着很是重要的做用,而且宣稱無需使用任何類選擇器或者ID選擇器。

但伴隨着越深刻的開發,我越會遠離這種複雜的選擇器。一個選擇器越複雜,與HTML就越耦合。依靠HTML標籤和組合器能夠保持HTML代碼乾乾淨淨,但卻讓CSS更加毛重和凌亂。

#main-nav ul li ul li div { }  #content article h1:first-child { }  #sidebar > div > h3 + p { }

對上面代碼進行簡單的理解。第一個多是對下拉菜單進行樣式化;第二個想說明文章的主標題應該與其餘頁面的H1元素不一樣;最後一個表示在第一段的側邊欄區域添加一些額外的空間。

若是這個HTML是永遠不變的,那就無可說之處,但這根本絕不現實。過於複雜的選擇器會讓人印象深入,它可讓HTML擺脫掉表面上的複雜,但對於實現良好的CSS架構目標卻毫無用處。

上面提到的例子都是不具有可預測性、可重用、可擴展和可維護這四大特性的。例如第一個選擇器(下來菜單)例子,若是一個外觀很是類似的下拉列表須要用在不一樣的頁面上,而且#main-nav並不屬於內部元素,那麼你是否須要從新設計?假設開發者想要修改第三個例子裏div裏面部分標記,那麼整個規則都會被打破。

過於通用的類名

當建立可重用的設計組件時,在組件的類選擇器中覆蓋附件的子元素是很常見的現象。例如:

<div class="widget">    <h3 class="title">...</h3>    <div class="contents">      Lorem ipsum dolor sit amet, consectetur adipiscing elit.      In condimentum justo et est dapibus sit amet euismod ligula ornare.      Vivamus elementum accumsan dignissim.      <button class="action">Click Me!</button>    </div>  </div>
.widget {}  .widget .title {}  .widget .contents {}  .widget .action {}

像.title、.contents、.action這些子元素類選擇器能夠被安全地進行樣式命名,無需擔憂這些樣式會蔓延到擁有相同類名的其餘元素中。這是千真萬確的。但它並無阻止相一樣式類名稱會蔓延到這個組件上。

在一些大型項目上,像.title這樣的名稱頗有可能會被用在另一個頁面或者自己。若是這樣的狀況發生,那麼整個標題部分明顯會和預期的不同。

過於通用的類選擇器名稱會致使許多不可預測的CSS樣式發生。

一個規則作太多事

有時,你要在網站的左上角區域作一個20pixels的可視化組件。

.widget {    position: absolute;    top: 20px;    left: 20px;    background-color: red;    font-size: 1.5em;    text-transform: uppercase;  }

下面,你須要在網站的其餘區域使用該組件,那麼上面的這個代碼明顯是錯誤的,不可重用的。

問題的關鍵是你讓.widget這個選擇器作的事情太多,不只對該組件的位置進行了規定,還對它的外觀和感受方面進行了樣式。外觀和感受能夠通用,而位置是不能夠的。有時候,把它們整合起來使用反而會大打折扣。

雖然這些看起來並沒有害處,對一些缺少經驗的CSS程序員來講,複製和粘貼已經成爲一種習慣。若是一個新團隊須要一個特定組件,好比.infobox,他們會嘗試使用這個類選擇器。但若是該信息框沒有按照指望的那樣,在每一個須要的地方正確顯示出來。這時,你認爲他們會怎麼作?以個人經驗來看,他們會打破可重用這一規則,相反,他們會簡單地把這些代碼複製粘貼到每一個須要的地方。作些沒必要要的重複工做。

3.緣由

上面列舉的這些常規錯誤實踐都有一個類似性,CSS樣式承擔過多。

對這樣的說法你會感到奇怪,畢竟,它是一個樣式表,難道不該該承擔大多數(若是不是所有)的樣式嗎?那不正是咱們想要的嗎?

的確。可是一般來說,事情並無那麼簡單。內容與表現(presentation)相分離是件好事,但CSS從HTML中獨立出來並不意味着內容也須要從表現中分離。換句話說,若是CSS請求深刻分析HTML架構,那麼從HTML中分拆全部的顯示代碼並不必定會實現全部的目標。

此外,HTML不多會只包含內容,也表示總體框架。一般,架構是會包含container元素,容許CSS隔離一些固定元素。即便沒有表象類(presentational classes),也能混合HTML清晰地把內容展現出來。

我相信,鑑於當前的HTML和CSS狀態,把HTML和CSS明智地結合起來,當作表現層是很是須要的。而經過模板和局部模板(partials)也能夠把內容層進行分離。 

4.解決方案。

若是把HTML和CSS結合起來,做爲一個Web應用程序的表現層,那麼它們須要採起一些方式更好地促進優秀CSS架構的造成。

最好的方法是CSS中儘量少的包含HTML架構。CSS則是應該定義元素的視覺效果,不管該視覺元素在哪裏。若是有一些特定的組件須要在不一樣的場合顯示不一樣的效果,那麼應該賦予不一樣的名稱。例如,CSS經過.button類選擇器定義了一個按鈕組件。若是HTML想要一個特定的元素看起來像按鈕,那麼就可使用.button。若是這裏有特殊要求,這裏的按鈕與其餘的有所不一樣(有可能更大和寬些),那麼CSS須要定義一個新的類,HTML可使用新的類來賦予該元素新的視覺效果。

CSS賦予元素的外在特徵,HTML在頁面上進行調用。更少的CSS能被更多的HTML架構調用是最好的。

準確地在HTML中聲明元素不只能夠清晰表達設計意圖,其餘開發者也能夠清晰地查看標記而且知道元素將呈現的樣子。若是沒有這種實踐,它很難區分一個元素的外觀設置是有意或無心的,這樣很容易致使團隊混亂。

在標記中填入大量的類(classes)是種常見缺陷,這樣作每每須要花費額外的精力。一個CSS樣式能夠給一個特定組件引用上千次。那麼,爲了在標記裏面進行顯示聲明,就真的值得去重複編寫這樣的類嗎?

雖然這種擔憂是有效的,但它可能會產生誤導。言下之意就是不管你在CSS中使用一個父選擇器仍是親手編寫上千個Class,這裏都會有些額外的選擇。在Rails或者其餘框架裏查看同級別抽象很大程度上能夠在HTML中保持很好的視覺外觀,而且無需在類中一遍又一遍地編寫相同的類。

5.最佳實踐。

針對上面的種種錯誤,我進行了很好地總結,而且根據自身經驗提出了一些建議,但願它們能幫助您更好地實現良好的CSS架構目標。

專一

確保選擇器對一些元素不進行無關樣式的最好方法是不給它們機會。例如像#main-nav ul li ul li div這樣的選擇器可能很容易地應用於不想要的元素上。另外一方面,像.subnav這樣的選擇器就不會給它們任何機會。把類選擇器直接應用於你想要的元素上是最好的方式,而且能夠保持元素的可預測性。

/* Grenade */ #main-nav ul li ul { }   /* Sniper Rifle */ .subnav { }

模塊化

一個組織結構良好的組件層能夠幫助解決HTML架構與CSS那種鬆散的耦合性。此外,CSS組件自己應該是模塊化的。組件應該知道如何進行樣式和更好地工做,可是關於佈局、定位以及它們與周圍元素的關係不該該作太多的假設。

通常而言,CSS要定義的應該是組件的外觀,而不是佈局或者位置。一樣在使用background、color和font這些屬性時也要遵循原則使用。

佈局和位置應當由一個單獨的佈局類或者單獨的容器元素構成(請記住,有效地把內容與展現進行分離其實就是把內容與容器進行分離)。

給類進行命名空間

咱們已經檢查出爲何父選擇器不能在封閉和防止交叉樣式污染上面發揮100%的功效。而一個更好的解決方案就是在類上應用命名空間。若是一個元素是可視化組件的一員,那麼該元素的每一個子元素都應該使用基於命名空間的組件。

/* High risk of style cross-contamination */ .widget { }  .widget .title { }   /* Low risk of style cross-contamination */ .widget { }  .widget-title { }

給類進行命名空間能夠保持組件獨立性和模塊化。它能夠把現有類衝突降至最小而且減小子元素的一些特殊要求。

建立修飾符類來擴展組件

當一個現有組件須要在一個特定的語境中有所不一樣時,能夠建立一個修飾符類(modifier class)來擴展它。

/* Bad */ .widget { }  #sidebar .widget { }   /* Good */ .widget { }  .widget-sidebar { }

正如咱們看到的,基於父元素的缺點對組件進行修改,須要重申:一個修飾符類能夠在任何地方使用。基於位置的覆蓋只能被用在一個特定的位置,修飾符類也能夠根據須要被屢次使用。顯然,修飾符類是符合HTML開發者需求的。

把CSS組織成邏輯結構

Jonathan Snook在其很是優秀的《SMACSS》書中提到,CSS能夠被分紅四個不一樣的類:基礎(base)、佈局(layout)、模塊(modules)和狀態(state)。基礎包括了復位原則和元素缺省值;佈局是對站點範圍內的元素進行定位以及像網格系統那樣做爲一種通用佈局助手;模塊便是可重用的視覺元素;狀態即指樣式,能夠經過JavaScript進行開啓或關閉。

組件是一個獨立的視覺元素。模板在另外一方面則是構建塊。模板不多獨自站在本身的角度去描述視覺和感受,相反,它們是單一的、可重用的模式,能夠放在一塊兒造成組件。

爲了提供更詳細的例子,一個組件可能就是一個模式對話框。該模式可能在頭部包含漸變的網站簽名、或者在周圍會有陰影、在右上角會有關閉按鈕、位置固定在垂直與水平線中間。這四個模式可能被網站重複屢次使用,因此在每次使用的時候,你都不多會想到從新編碼與設計。這些全部的模板即造成了一個模塊組件。

因樣式和風格使用類

有過大型網站建設的人可能有個這樣的經驗,一個擁有類的HTML元素可能徹底不知道其用途。你想刪除它,可是又猶豫不決,由於它的做用你可能還未意識到。一旦這樣的事情一遍又一遍發生的時候,隨着時間的推移,項目中將會有愈來愈多這樣的類,只由於團隊成員都不敢刪除。

在Web前端開發中,類承擔了太多的責任,所以纔會產生這樣的問題。樣式化HTML元素、扮演着JavaScript hook角色、功能檢測、自動化測試等。當這麼多應用程序在使用類時,讓你從HTML中刪除它們將會變的很是艱難。

然而,使用一些成熟的約定(慣例)便可徹底避免這種問題。當在HTML中看到一個類時,你應該當即明白它的目的。我建議在前面使用前綴,例如用於JavaScript的在前面加.js,表示Modernizr classes能夠在前面加.supports,沒有加前綴的即用於表示樣式。

這樣來發現未使用的類和從HTML中移除它們將會變得很是簡單。你甚至能夠自動完成這一個過程,在JavaScript中經過交叉引用HTML中的document.styleSheets對象。若是在document.styleSheets中沒有發現該類,便可安全移除。

通常來講,最佳作法是把內容與演示相分離,另外把功能分離開來也一樣重要。使用樣式類像JavaScript hook在某種程度上能夠加深CSS與JavaScript之間的耦合,但在不打破功能性的前提下很難或者根本不可能更改外觀。 

有邏輯的命名類

大多數寫CSS的人喜歡使用連字符來分隔命名詞,但連字符並不足以區分不一樣類型之間的類。

Nicolas Gallagher最近針對遇到的問題寫了一個解決方案,而且取得了巨大的成功(略有改動),爲了說明命名約定,能夠考慮如下格式:

/* A component */ .button-group { }   /* A component modifier (modifying .button) */ .button-primary { }   /* A component sub-object (lives within .button) */ .button-icon { }   /* Is this a component class or a layout class? */ .header { }

從上述類中能夠發現其很難正確區分類型規則。這不但會困惑,並且連自動測試CSS和HTML也變的很難。一個結構化的命名約定應該是初看就可以知道其類名與其餘類之間的關係,而且知道它出如今HTML中的位置——使命名更加簡單和容易測試。

/* Templates Rules (using Sass placeholders) */ %template-name  %template-name--modifier-name  %template-name__sub-object  %template-name__sub-object--modifier-name   /* Component Rules */ .component-name  .component-name--modifier-name  .component-name__sub-object  .component-name__sub-object--modifier-name   /* Layout Rules */ .l-layout-method  .grid   /* State Rules */ .is-state-type   /* Non-styled JavaScript Hooks */ .js-action-name

重作第一個例子:

/* A component */ .button-group { }   /* A component modifier (modifying .button) */ .button--primary { }   /* A component sub-object (lives within .button) */ .button__icon { }   /* A layout class */ .l-header { }

6.工具

維護一個高效且組織良好的CSS架構是很是困難的,尤爲是在大型團隊中。下面向你們推薦幾款很好的工具來幫你管理網站CSS架構。

CSS Preprocessor

CSS預處理器採用PHP5編寫,有預處理器的常見功能,能夠幫你快速編寫CSS。另外有些號稱「功能」的預處理器實際上並不會對CSS架構產生良好做用。下面我提供一個列表,在使用時必定要避免:

  • 切勿純粹爲了組織代碼來嵌套規則。只有當輸出你真正想要的CSS時才能夠。

  • 在無需傳遞參數的時候切勿使用mixin,不帶參數的mixin更適合用做模板,易擴展。

  • 切勿在選擇器上使用@extend ,它不是個單一的類。從設計角度來看是毫無心義的,它會膨脹編譯過的CSS。

  • 在運用組件修飾符規則時,切勿使用@extend UI組件,這樣會失去基礎鏈。

@extend%placeholder是預處理器裏面很是好的兩個功能。它們能夠幫你輕鬆管理CSS抽象而且無需添加bloat和大量的基類到CSS和HTML裏,不然將會很難管理。

當你初次使用@extend時,常會與修飾符類一塊兒使用,例如:

.button {    /* button styles */ }   /* Bad */ .button--primary {    @extend .button;    /* modification styles */ }

這樣作會讓你在HTML中失去繼承鏈。很難使用JavaScript選擇全部的按鈕實例。

做爲通常規則,不多去擴展UI組件或者在知道類型後作些什麼。這是區分模板和組件的一種方式,模板無需參與到應用程序的邏輯,而且可使用預處理器進行安全擴展。

下面是一個引用上面的模式例子:

.modal {    @extend %dialog;    @extend %drop-shadow;    @extend %statically-centered;    /* other modal styles */ }   .modal__close {    @extend %dialog__close;    /* other close button styles */ }   .modal__header {    @extend %background-gradient;    /* other modal header styles */ }

CSS Lint

CSS Lint是由Nicole SullivanNicholas Zakas編寫的一款代碼質量檢測工具,幫助CSS開發人員寫出更好的代碼。他們的網站上是這樣介紹CSS Lint的:

CSS Lint是一個用來幫你找出CSS代碼中問題的工具,它可作基本的語法檢查以及使用一套預設的規則來檢查代碼中的問題,規則是能夠擴展的。

使用CSS Lint建議:

  1. 不要在選擇器中出現ID。

  2. 在多部分規則中,不要使用非語義(non-semantic)類型選擇器,例如DIV、SPAN等。

  3. 在一個選擇器中使用的鏈接符(combinator)不要超過2個。

  4. 任何類名都不要以「js-」開始。

  5. 若是在非「I-」前綴規則裏常用佈局和定位應給予警告

  6. 若是一個類定義後被從新定義成子類,也應給予警告。

總結

CSS不只僅是視覺設計,也不要由於你編寫CSS就隨便拋出編程的最佳實踐。像OOP、DRY、打開/閉合、與內容分離等這些規則應該應用到CSS裏面。不管你如何組織代碼,都要確保方法真正幫助到你,而且使你的開發更加容易和可維護的。

來自:Appfolio Engineering Technical Blog

相關文章
相關標籤/搜索