CSS模組化(六) 模組化設計
6. 模組化設計
6.1 樣式的作用域──頁面重構中的模組化設計(一)
模組化設計我已經提過很多了,都是跟模組化相關的,不過之前一直沒有講到具體實現方面的內容,只是一些思維。這次重點講一下實現方面的內容,權當到目前為止我對模組化的一些總結整理。
要做好模組化,我覺得理解好樣式的作用域是很重要的。寫過程式的同學應該都知道,變數是有作用域的(不知道的同學自己去問谷歌,這裡就不作解釋了),樣式的定義也同樣存在著作用域的問題,即定義的作 用範圍,很容易就能理解,如下面的p的作用域:
CODE:
/*作用域:全域性*/ p{text-indent:2em;}
CODE:
/*作用域:.demo這個類中*/ .demo p{color:#000000;}
樣式選擇器的優先順序是學習樣式的基礎知識,一起簡單回顧下:
● 標籤的權值為0,0,0,1
● 類的權值為0,0,1,0
● 屬性選擇的權值為0,0,1,1
● ID的權值為0,1,0,0
● important的權值為最高1,0,0,0
使用的規則也很簡單,就是選擇器的權值加到一起,大的優先;如果權值相同,後定義的優先。雖然很簡單,但如果書寫的時候沒有注意,很容易就會導致CSS的重複定義,程式碼冗餘。從上面我們可以得出兩個關鍵的因素:
1)權值的大小跟選擇器的型別和數量有關
2)樣式的優先順序跟樣式的定義順序有關
瞭解樣式的權值後有什麼作用呢?比如可以這樣用:舉一個最簡單的例子
CODE:
body{color:#555555;}.demo{color:#000000;}
CODE:
<p>這裡的文字顏色受全域性定義的影響</p>
<div class="demo"><p>這裡的文字顏色受類demo定義的影響</p></div>
<p class="demo">這裡的文字顏色受類demo定義的影響</p>
知道了樣式的權值,你就知道上面例子的表現是怎樣的了。進一步的應用,就是模組化了。
再來說說“作用域”,相信大家很容易就會想到“全域性”、“公共”這些詞,關注過模組化的同學應該都知道,網上說得最多的一種“模組化”,就是像 header、footer這樣的以大區域劃分。在去年
我在這一塊的劃分上,有點類似克軍的“樣式的三層架構”,有一點小的差別,我是以“作用域”來分的:公共級(全域性)、欄目級 (區域性公共)、頁面級。如何劃分這個“作用域”呢?很簡單,全域性的global就是公共級的;只在欄目中用到的區域性global是屬於欄目級的;隻影響單個頁面的就是屬於頁面 級的了。
最後幾點要特別注意的:
除了標籤選擇器之外,哪些類是使用於公共級、欄目級中的,如
CODE:
.tx_hit{color:#FF0000 !important;}
的適用範圍是公共級的,應該放於全域性的定義中。但,如果它隻影響於某個欄目,那麼就應該把它放於欄目級的作用域中。
● 標籤選擇器一般屬於欄目定義,有時會用於公共級作用域中,除了最基礎的reset之外,應儘可能少使用在公共級定義中
● 可繼承的屬性定義使用時須注意影響的範圍,特別是在標籤選擇器中使用時
● 同類選擇器無加權
接下來的內容就是以這個為基礎的,希望大家能理解“樣式的作用域”,對於後繼內容的理解會很有幫助。
6.2 欄目級作用域──頁面重構中的模組化設計(二)
在《樣式的作用域── 頁面重構中的模組化設計(一)》中,我將樣式的作用域分為了三個部分:公共級(全域性)、欄目級(區域性公共)、頁面級。公共級(全域性)容易理解,即影響站點中所有頁面。簡單解釋下欄目級(區域性公共)和頁面級:
頁面級
可分為兩種情況:在多個頁面間,頁面級作用域指標對某一單獨的頁面定義;在同一個頁面中,頁面級作用指標對某 一標籤的定義。它將決定最終的頁面效果。
欄目級(區域性公共)
介於全域性與單個頁面之間的一個作用 域,影響一個欄目(或某區域)。通常以某一類選擇符做為開始,以包含選擇符的方式將樣式定義限定在某一區域中。
CODE:
/* 隻影響demo這個區域 */
.demo a{...}
.demo p{...}
.demo .title{...}
需要消化下的內容,決定一個樣式定義是屬於哪個作用域的因素有以下兩點:
樣式定義所在樣式檔案中的位置。(同樣的一個定義,放在不同的位置,所影響的範圍會有所不同。)
HTML中繫結demo這個類的標籤位置。(同樣一個類,繫結在body標籤和繫結在頁面中某個標籤上,所影響的範圍也會不同。)
在一個站點中,可能會分為幾個不同的欄目,同一個欄目中,一般風格會保持一致。而不同的欄目間,相似的風格則不一定會相同。即使是全站通用的模 塊,如翻頁,也可能會因為欄目的不同而會有一些差異,比如連結的顏色等等。使用欄目級的樣式定義,能很好的減少程式碼的冗餘,提高模組的複用性。
另外需要在思維上注意的一點,以作用域劃分,並不意味著有著對應的檔案,可能有些同學會習慣的以為一個作用域就應該對應著一個檔案。比如一個小的 欄目,可能只有兩三個頁面,這時我們就不一定需要再把欄目級的定義單獨出來一個檔案,而是與頁面級的定義一起放在一個檔案裡,像這樣:
CODE:
/* S 欄目級定義 */
.class{...}
/* E 欄目級定義 */
/* S 頁面級定義 */
.page{...}
/* E 頁面級定義 */
6.3 繼承──頁面重構中的模組化設計(三)
前面我們瞭解了樣式的作用域的分類和欄目級作用域。在權值中,還有一個很重要的因素,需要做下補充,起因是這樣的,有個同學在CSS森林群裡問了個問題:根據樣式權值兩個關鍵的因素
權值的大小跟選擇器的型別和數量有關
樣式的優先順序跟樣式的定義順序有關
可以知道,如果10個標籤選擇器的權值應該比一個類選擇的權值高,像這樣:
CODE:
div div div div div div div div div div div{color:blue;}
.c10{color:red;}
CODE:
<div class="c1">
<div class="c2">
<div class="c3">
<div class="c4">
<div class="c5">
<div class="c6">
<div class="c7">
<div class="c8">
<div class="c9">
<div class="c10">
<div>這段文字是什麼顏色?</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
CODE:
div{color:blue;}
.c10{color:red;}
是不是跟想的不太一樣?難道前面所說的權值是有問題的?前面講的權值並沒有問題,不過漏了一個重要的規則: 繼承的權值小於 0,0,0,1。
樣式的繼承
指被包在內部的標籤將擁有外部標籤的樣式性質。
繼承最大的意義在於可以減少重複的定義,比如要定義整個頁面的文字顏色,只需要定義body的color樣式,body裡的所有標籤都會繼承 body的color定義。是不是很方便?方便是相對的,當你想要為body內部分標籤定義另一種文字顏色時,繼承也許會成為增加重複定義、降低效能的禍 首。
並不是所有的樣式定義都具有繼承的性質,整理了一下常用有繼承性的定義, 這些定義在使用的時候要比較注意。
簡單分析下上面的例子,最後一部分的程式碼:
CODE:
<div class="c10">
<div>這段文字是什麼顏色?</div>
</div>
當定義了c10後,根據權值,類定義的權值是0,0,1,0,應該是比div這個定義0,0,0,1要高的,但由於div是直接定義到標籤上的, 比起從c10的定義中繼承來的定義權值更高。稍微改下就清楚了:
CODE:
<div class="c1">
<div class="c2">
<div class="c3">
<div class="c4">
<div class="c5">
<div class="c6">
<div class="c7">
<div class="c8">
<div class="c9">
<div class="c10">
<p>這段文字是什麼顏色?</p>
<div>這段文字是什麼顏色?</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
從修改後的例子 可以看到,p標籤繼承了c10的定義,顯示為紅色。因此,在使用標籤選擇器的時候,應特別注意它的作用域,個人的建議是,除了最基本的reset之外,在 公共作用域中最好不要使用標籤選擇器,在欄目級作用域中也應儘可能的少用。
常用有繼承性的樣式定義:
● text-indent
● text-align
● layout-flow
● writing-mode
● line-break
● white-space
● word-wrap
● list-style
● list-style-image
● list-style-position
● list-style-type
● font
● font-style
● font-variant
● font-weight
● font-size
● line-height
● font-family
● color
● text-transform
● letter-spacing
● word-spacing
6.4 模組化的核心思想──頁面重構中的模組化設計(四)
有不少同學覺得前面的內容過於簡單了,對於樣式的作用域的分類、欄目級作用域、繼承等內容的確十分基礎,不過基礎還是很重要的。下面就一起進入這個系列真正的主題——“模組化”吧。
早在Qzone4.0的頁面架構中已經在專案中開始摸索提高程式碼複用的方法,只不過當時並沒有很清晰的認識到“模組化”這個思想。從去年的《從宜家的傢俱設計 講模組化》開始,模組化成了我主要的一個學習方向。藉著無數的提問、思考、討論,漸漸形成了一個比較清晰的、較為完整的方案。後面的內容,更多的是出於我在實際項 目中總結出來的方法,雖然已經儘可能為出現的問題提供瞭解決方法,不過還是少不了會有些我沒遇到過或沒考慮到的,歡迎各位指出。
首先來了解下頁面重構中模組化的核心思想:將HTML和CSS通過一定的規則進行分類、組合,以達到特定HTML、CSS在特 定範圍內最大程度的複用。有三個關鍵詞:規則、特定範圍、最大程度的複用。怎麼理解呢?
規則
編寫模組時需要遵循的規範
特定範圍
模組可使用的範圍。與樣式的作用域有關,大部分模組的使用範圍僅僅是某一個欄目或站點。
最大程度的複用
做最少的修改即可重複使用。很多同學都把“複用”理解成不用修改的直接使用,但在頁面 製作中,由於實際的專案環境,基本是不可能做到“一個模組走天下”的。不同的欄目會有不同的需求,大家應該都多少有所體會,我就不多講了。
從實際出發,才能最終服務於實際。我們知道一個HTML標籤可以繫結多個樣式,所以我們可以這樣去定義一個模組:
CODE:
<div class="class-a class-b class-c">
...
</div>
不少同學已經知道這個方法了,而且還很形像的稱之為“拼樣式”。這樣的定義很容易引出其它的問題,比如樣式類的個數多少個適合?樣式類如何命名? 等等。下面講下我的方法,從前面我們學到的樣式作用域及模組化的核心思想,我們可以把樣式進行一個分類,像這樣:
CODE:
.mode-a{/* 定義一個模組 */}
.type-a{/* 模組中的差異化定義 */}
.mode-name{/* 針對單個模組的個性化定義 */}
CODE:
<div class="mode-a type-a mode-name">
...
</div>
上面的“mode-a”,我稱它叫為“基類”;“type-a”為“擴充套件類”;“mode-name”為“模組名”,作用分別是:
基類
(基礎樣式)模組的基礎表現。包含了模組中大部分的狀態。
擴充套件類
(擴充套件樣式)用於對使用基類的模組進行小範圍的修改
模組名
模組在某一作用域中的唯一名稱。
這裡有一個簡單的例子 可以幫助理解。
也有同學主張用ID去表示“模組名”,我認為這種方式擴充套件性比較差,而且很容易與開發的ID衝突,不過也不失為一個方法。
6.5 基類、擴充套件類──頁面重構中的模組化設計(五)
基類和擴充套件類是這個系列的主要內容,上一篇《模組化的核心思想──頁面重構中的模組 化設計(四)》中只是簡單提了一下,我們再深入的來了解下它們。
一般所使用的“模組化”的方法,就是以某一個類做為定義的開始,比如:
CODE:
/* S 圖片列表 */
.pic_lists li,
.pic_lists li img{float:left;width:122px;height:122px;margin-bottom:8px;}
.pic_lists li{list-style:none;margin:0 0 0 6px;text-align:center;}
.pic_lists li .pic{display:block;border:1px solid #476081;}
/* E 圖片列表 */
/* S mtv列表 */
.mtv_lists{width:930px;height:130px;}
.mtv_lists li,
.mtv_lists li img{float:left;width:120px;margin-bottom:8px;}
.mtv_lists li{list-style:none;margin:0 10px 0 0;text-align:center;}
.mtv_lists li img{height:90px;border:1px solid #476081;}
.mtv_lists li .pic{display:block;width:120px;height:90px;margin-bottom:8px;}
/* E mtv列表 */
這個例子:兩個列表模組 。這種方式是比較常見的,可以很好的將一個模組獨立出來。如果使用新學習到的“方法”來寫這兩個列表模組,應該是怎樣?
基類(基礎樣式)模組的基礎表現。包含了模組中大部分的狀態。也就是說,當出現多個類似的模組時,基類包含了這些模組的大部分的效果(或者理解為公共的部分),在基類的基礎上,我們可以通過新增很少的程式碼 ——擴充套件類,來達到所需要要效果。像這樣:
CODE:
/* S 列表 基類 */
.mode_lists li,
.mode_lists li img{float:left;width:122px;margin-bottom:8px;}
.mode_lists li{list-style:none;margin:0 10px 18px 0;text-align:center;}
.mode_lists li img{border:1px solid #476081;}
/* E 列表 基類 */
/* S 圖片列表 */
.pic_lists li,
.pic_lists li img{height:122px;}
.pic_lists li{margin:0 0 8px 6px;}
.pic_lists li .pic{display:block;border:1px solid #476081;}
/* E 圖片列表 */
/* S mtv列表 */
.mtv_lists{width:930px;height:130px;}
.mtv_lists li,
.mtv_lists li img{width:120px;height:90px;}
.mtv_lists li .pic{display:block;margin-bottom:8px;}
/* E mtv列表 */
可能你會覺得這樣的樣式不就多寫了,還得把原先的模組類變成兩個。的確不是所有的模組都值得這樣去做,於是我們可以得到一種“偷懶”的作法,把其 中一個模組直接變成基類。對於經常會被使用的模組,像圖片列表、播放列表等,這種寫法在程式碼的複用和效率會有一定的提高。一般情況下只需要做下簡單的修改 即可應用,來看一個複雜些的例子:
一個帶頭像的訊息列表(A)
看看這兩個圖,在腦中先想想如果是你,你要怎麼實現。……5分鐘過去了……差不多有方案了,按上面的思路,基類是包含了大部分的效果的,也就是說 基類應該能滿足大部分效果的需要,兩個模組間差異的地方,可以通過擴充套件類來完成。當然前提是這兩個模組有能找到類似的點,能夠形成基類。
在這兩個模組中,我們不難看出,A模組和B模組在資訊的部分是很類似的,雖然B模組的列表不需要A模組的評論部分,但這並不影響B模組的表現。所 以我們可以把這兩個模組看成的類似模組。另個,以哪個為基類呢?從滿足大部分效果這個要求來看,很明顯A模組做為基類是要比B模組做為基類更合適的,如果 用B模組做基類,那麼需要寫更多的擴充套件類來滿足A的需要。另外還有一個重要的點,之所以選擇A模組為基類,是因為A在欄目中被更多的頁面使用。
OK,來看看A模組怎麼實現(樣式部分):
CODE:
/* S 訊息 基類 */
.mode_message{position:relative;padding:8px 3px 8px 48px;
border-bottom:1px solid #DAECF6;_zoom:1;line-height:1.3;}
.mode_message .user_info{position:absolute;left:3px;top:10px;}
.mode_message .user_info .pic img{width:35px;height:35px;}
.mode_message .mode_message_cont{color:#797979;
word-break:normal;word-wrap:break-word;}
.mode_message .mode_message_cont .info{display:block;zoom:1;}
.mode_message .mode_message_cont .info .music_name{color:#22639B;}
.mode_message .mode_message_cont .info .op_music{display:none;}
.mode_message .mode_message_cont .info:hover .op_music,
.mode_message .mode_message_cont .info.hover .op_music{display:block;position:absolute;right:5px;top:7px;
background-color:#FFFFFF;}
.mode_message .msg{padding:2px 0;word-break:normal;word-wrap:break-word;}
.mode_message .mode_message_cont .op{margin-bottom:3px;}
.mode_message .time{display:inline-block;*display:inline;*zoom:1;font-size:10px;}
.mode_message .msg .p_zt_l,.mode_message .msg .p_zt_r{display:inline-block;*display:inline;*zoom:1;
width:13px;height:8px;background:url(img/_g_other.png) no-repeat -17px -17px;
vertical-align:text-middle;*vertical-align:middle;}
.mode_message .msg .p_zt_r{background-position:0 -28px;}
.mode_message .write_back .cont{margin-bottom:2px;padding:5px;background-color:#EAF6FA;_zoom:1;}
.mode_message .write_back .cont .cont{border-left:1px solid #ABCFE1;}
.mode_message .write_back .cont .zt{*overflow:hidden;}
.mode_message .write_back .cont .zt2{*padding-right:6px;}
.mode_message .write_back .cont .zt textarea{width:100%;height:40px;padding:0 2px;
border:1px solid #D1E1EC;line-height:20px;color:#4F4F4F;}
.mode_message .write_back .cont .zt .normal textarea{height:23px;color:#B1B4B8;}
.mode_message .write_back .cont .zt .normal .op{display:none;}
.mode_message .write_back .cont .op{margin:5px 0 0;}
.mode_message .write_back .cont .op .bt_v2{padding:0 2px;vertical-align:middle;}
.mode_message .write_back .cont .zt{width:98.5%;*width:99.9%}
.mode_message:nth-last-child(1){border-bottom:none;}
/* E 訊息 基類 */
別忘了提示條,雖然是用於模組中,但它應該是可以被更廣泛使用的模組,因此我把它單獨提了出來:
CODE:
/* S 提示條 基類 */
.mode_hint{position:relative;margin:3px 0;padding:5px;
background-color:#FFFEAB;color:#000000;_zoom:1;}
.mode_hint .op{position:absolute;right:8px;top:5px;}
.mode_hint .op a{color:#000000;}
/* E 提示條 基類 */
還有像按鈕、全域性定義這些內容,就不列出了。完整的可以看:基類、擴充套件類例項 。例子中可以看到,擴充套件類的定義很少,只是一些簡單的定義,像B模組:
CODE:
/* S 訊息 擴充套件 */
.message_nopic{padding-left:0;}
/* E 訊息 擴充套件 */
只需要一句,將頭像去掉即可。
6.6 CSS模組的註釋——頁面重構中的模組化設計(六)
從前面的內容我們已經知道,樣式是可以分成各個模組去寫的,如何表示各個模組的作用及它們之間的關係呢?CSS的註釋是不二的選擇。
與普通的註釋不同,模組的註釋需要一些更詳細的內容,比如:功能說明、模組版本、關聯資訊等等。 像《基類、擴充套件類──頁面重構中的模組化設計(五)》中例子的註釋,顯然是比較簡單的。為了減少不必要的溝通,我們可以使用較為固定的格式去完成這個註釋。
舉個例子:
CODE:
/**
* @name:mode_name
* @author:ghostzhang
* @version:1.0
* @type:基類
* @explain:Demo
*/
.mode_name{...}
.mode_name h2{
...
}
.mode_name .cont{
...
}
/* @end **/
/**
* @name:mode_name_b
* @author:ghostzhang
* @version:1.0
* @type:擴充套件類
* @explain:Demo
* @dependent:mode_name
*/
.mode_name_b{...}
.mode_name_b h2{
...
}
.mode_name_b .cont{
...
}
/* @end **/
從註釋中就可以知道mode_name_b和mode_name_a之間的關係。
主要的關鍵字有:
@name
標明模組的名稱
@author
標明模組的作者
@version
標明該模組的版本
@explain
功能說明
@relating
標明該關聯的模組
@dependent
標明該所依賴的模組
@type標明該模組的型別:公共、基類、擴充套件類
需要注意的規則:
以“/**”標記模組的開始
從“/**”到第一個“*/”作為模組相關資訊的說明,包含關鍵字
關鍵字以[email protected]==開頭,“:”後開始到“*”的內容為相關的值,即:
@關鍵字:值*
以“/* @end **/”標記模組的結束
模組註釋內不可巢狀
提供了一個小工具(cssModeCODE: )幫助大家填寫樣式模組的註釋。