標籤:
第一次聽到“CSS模組化”這個詞是在WebReBuild的第四屆“重構人生”年會上,當時我還想,“哈,CSS也有模組化,我沒聽錯吧?”事實上,我沒聽錯,你也沒看錯,早就有CSS模組化這個概念了。之所以我把這個拿出來討論,是因為一個算是比較大的項目出現了一些重構上的危機,因此引發了我們對頁面重構中的CSS進行模組化的思考。
首先,什麼是CSS模組化?在談CSS模組化之前我們先看一下百度百科對模組化的解釋:
“模組化是指解決一個複雜問題時自頂向下逐層把系統劃分成若干模組的過程。每個模組完成一個特定的子功能,所有的模組按某種方法組裝起來,成為一個整體,完成整個系統所要求的功能。在系統的結構中,模組是可組合、分解和更換的單元。”
我對上面一段話的理解是,模組化就像我們玩樂高積木一樣,一個積木就是一個單元,可以通過組合不同的積木來搭建一個玩具模型。對於一個玩具模型,也 可以更換部分積木而迅速變成另一個模型。而CSS模組化的關鍵,就是如何合適地構建這些單元,也就是一個一個的class,以及如何靈活運用這些單元來達 到我們所要的呈現效果。
說了一堆繁雜的話,連我自己都覺得乏味。作為一個熱愛編程的頁面仔,我想借用物件導向的三大特性來說明CSS模組化——封裝、繼承、多態。
一、 封裝
HTML代碼
<div class=”module-a”>
<h3>標題1</h3>
<p>標題文字</p>
</div>
<div class=”module-b”>
<h3>標題2</h3>
<ul>
<li>列表</li>
<li>列表</li>
<li>列表</li>
</ul>
</div>
CSS代碼
.module-a{.....}
.module-a h3{.....}
.module-a p{.....}
.module-b{.....}
.module-b h3{.....}
.module-b ul{.....}
.module-b ul li{.....}
.module-b ul li{.....}
.module-b ul li{.....}
封裝是實現CSS模組化的最基本要求,封裝成的各個單元就是基本的CSS模組,可靈活用於組建頁面的各種顯示樣式。
二、 繼承
HTML代碼
<div class=” module module-A module-A-a”></div>
<div class=” module module-A module-A-b”></div>
CSS代碼
.module {.....}
.module-A {.....}
.module-A-a {.....}
.module-A-b {.....}
.module-B {.....}
.module-B-a {.....}
.module-B-b {.....}
繼承可謂是CSS模組化的關鍵所在,由於html元素可以擁有多個類,而且根據優先順序,後面定義的屬性可以覆蓋前面的,正因為這樣,繼承這個特性發 揮了巨大的作用。對於某些多數樣式屬性相同,僅有幾個不同樣式屬性的定義,可以用這個方法來實現。也可以在不改變某個通用樣式類的同時,用HTML調用複 合類,突出局部特例。
三、 多態
HTML代碼
<div class=”module-A”></div>
<div class=”part-A ”>
<div class=”module-A”></div>
</div>
<div class=”part-B ”>
<div class=”module-A”></div>
</div>
<div class=”part-C ”>
<div class=”module-A”></div>
</div>
CSS代碼
.module-A {.....}
.part-A .module-A {.....}
.part-B .module-A {.....}
.part-C .module-A {.....}
多態主要用於同一模組在頁面的不同部分或者不同頁面之間呈現出不同的樣式。對於CSS的多態,一個很經典而且常用的例子就是導覽列。我們知道,導航 欄往往需要標識出當前頁面所在的導航連結。也就是說,對於導覽列的連結,通常有兩種形態,一種是常態,一種是高亮態,比如處於首頁時,首頁的連結就是高亮 態的,其他導航連結是常態的。這個時候,很多人喜歡在li上加上一個current類,用來把該導航連結設定為高亮態,但是我覺得這種方式並不理想。
為什麼呢?因為對於結構一樣的導覽列,後台人員在處理時,往往是抽出成一個公用結構,然後各個頁面都引入這個結構。這對於後期的維護是相當有效, 因為涉及導覽列的修改僅需修改一次即可,如果不這麼做,那麼修改的次數將會等於頁面的個數,而且容易出錯。對於這種情況,多態就可以很好地解決,因為我們 可以在每個頁面的body(或者其他地方)加上一個類來標識不同的頁面(此類可以不定義任何樣式)。那麼,根據多態的特性,我們就可以為不同頁面的導航設 置不同的形態,同時又保持了導航連結的結構一致性。舉例如下:
HTML代碼
首頁:
<body class=”index”>
<ul class=”nav”>
<li class=”index”><a href=”#”>首頁</a></li>
<li class=”page1”><a href=”#”>內頁1</a></li>
<li class=”page2”><a href=”#”>內頁2</a></li>
</ul>
</body>
內頁1:
<body class=”page1”>
<ul class=”nav”>
<li class=”index”><a href=”#”>首頁</a></li>
<li class=”page1”><a href=”#”>內頁1</a></li>
<li class=”page2”><a href=”#”>內頁2</a></li>
</ul>
</body>
CSS代碼
//定義常態
.nav{.....}
.nav .index{.....}
.nav .page1{.....}
.nav .page2{.....}
//定義高亮態
.index .nav .index{.....}
.page1 .nav .page1{.....}
.page2 .nav .page2{.....}
這個就是多態的經典應用之一。
————————————補充部分————————————
joe哥指正,以上的這種對導航的處理辦法不太好,因為導航是一個公用群組件,所謂公用,就是應該結構和樣式都是公用的,上面的那種方法沒有做到樣式上公 用,因為很明顯定義高亮態那裡需要用到其他頁面的樣式類,而且joe哥提到可移植性的問題,如果這個導航搬到其他地方,那麼就需要為新的頁面的body全 部都添加一個類(這個我真沒想過導航還可以給多個網站重用,我眼光太短淺了)。那麼,究竟什麼做才是比較主流的呢?加current,沒錯,就是加 current,這個被我一開始就否決的方法。那麼又回到原來的問題,既然我們是引入同一個導航檔案,那麼current能加到哪裡呢?joe哥提出一個 被我們都忽略了的方法,指令碼!可以是js,可以是php,原來我們可以動態添加current類到導航去,驚訝吧!
好吧,我承認我有點過於興奮了,大伙兒看例子吧~
HTML代碼
<ul class=”nav”>
<li class=”index” id="index"><a href=”#”>首頁</a></li>
<li class=”page1” id="page1"><a href=”#”>內頁1</a></li>
<li class=”page2” id="page2"><a href=”#”>內頁2</a></li>
</ul>
CSS代碼
//定義導航的樣式
.nav{.....}
.nav li.index{.....}
.nav li.page1{.....}
.nav li.page2{.....}
//定義高亮態
.nav li.current{.....}
JS代碼
function addCurrent(class,html_id){
//class為增加的樣式類名,html_id為需要增加樣式類的html元素的id
document.getElementById(html_id).addClass(class); //這裡的class就是‘current’,addClass方法的實現我這裡就不多說了,你可以用jquery或者別的,甚至自己寫
}
這種做法對於不同的頁面,或者新增的頁面,只需改js的一個參數即可,做到了把導航部分真正實現模組化、組件化,公用的部分就應該公用到底,不留一 點餘地。不過我還想說一點是,如果你的current(高亮態)不是單獨的換個背景顏色之類的,而是用雪碧圖這類的方法,每個導航連結的current類 都是不同的,那麼以上的這種方法,貌似還有待改進。
————————————補充部分結束————————————
事實上應該指出,我這裡討論的只是CSS模組化的一部分,可以說是比較微觀的一部分。還有一些對CSS進行比較宏觀地劃分模組的說法,比如按頁面來劃分,一個頁面或者說一個地區為一個模組,比如像“幸福收藏夾”上所說的劃分方法:
網站的劃分標準更大一些,是以網站的專題為模組,不幸的是,很多專題的頁面風格都是類似的,導致各個模組間存在大量的冗餘代碼,這意味著對樣式的改動將會相當麻煩,一個地方的改動需要同時修改多個css檔案。
表面上看,雖然對CSS模組劃分得細是一個不錯的想法,但並不是越細越好。現在的一些開源系統,如dedecms,甚至說YUI架構的1.0版本, 都流行一種做法,就是把CSS模組細分成單屬性,比如把寬度960px定義為.w960,把清除浮動定義.clear,把字型綠色定義為.green等 等。
這樣做的好處是可以對頁面元素的樣式進行快速組裝,通過引入多個類便可以達到我們想要的效果。但是,這樣的做法會導致另外一個問題。大家想,這麼做 與內聯樣式有什麼區別?如果有一天,我們想要把多個地方的字型顏色由綠色改為紅色,那麼我們要做的不是改動css的color屬性,而是把這些html元 素的class都由green改為red。請注意,我們的初衷是改變的頁面表現,但最終我們需要改變結構來達到我們的目的,這樣就做不到結構與表現的分離 了。
究竟怎麼樣劃分CSS模組,又怎麼來用這些模組來組建頁面元素才是合適,其實很難找到一個標準,這不僅需要結合具體的頁面要求,還需要一定的經驗。
以上純屬個人見解,如有錯誤,歡迎指正。
談CSS模組化【封裝-繼承-多態】