結構化CSS設計思維
LESS、SASS等預處理器給CSS開發帶來了語法的靈活和便利,其本身卻沒有給我們帶來結構化設計思維。很少有人討論CSS的架構設計,而很多框架本身,如Bootstrap確實有架構設計思維作為根基。 要理解這些框架,高效使用這些框架,甚至最后實現自己的框架,必須要了解結構化CSS設計思想。
我不是前端專家,但是我想,是否一定要等成為了專家才能布道?那是不是太晚了。 所以我是作為一個CSS的學習者,給其他CSS學習者分享一下結構化CSS設計的學習心得。 我更多的是一個后端開發者,后端開發的成熟思想必定能給前端帶來新鮮血液。
前言
CSS從根本來講就是一系列的規則,對Html(作為內容標識和呈現)的風格化得一系列規則,因此,語法級別也就是兩個重要因素:選擇器和應用的風格。 簡單而超能。
一開始,CSS很容易學習,容易上手。當我們的網站成長為企業級網站時,我們的CSS開始成為臃腫、重復和難以為維護的一個混沌 。這種狀態在軟件開發中被稱為Big ball of mud ,更為常見的講法是面條式代碼,是一種缺乏良好架構和設計的表象。 解決這個問題的方法和思維在后臺軟件開發中已經是比較多的討論也相對成熟。同為軟件開發的CSS開發,毋庸置疑,卻可以這些借鑒思維。
面向方面
如前所述,CSS只是一系列規則,這是對CSS的初步認識,但我們不能停留在這個認識。如同,我們認識到所有物體都由原子組成,但是這個認識并不完全能夠替代化學在分子級別的研究。
這些規則必須要進一步分化,各自有不同作用和角色,不能平面化。表面上同是選擇器+風格化的CSS規則,根據它們的業務意義,應該劃分為不同的類別或者方面 (Aspect 參考用詞Aspect-oriented programming)。
這些方面分別是:
- 基本(元素)
- 布局
- 模塊
- 狀態
- 主題
明確的分析你要寫的CSS規則屬于哪個方面,在實現上區分這些方面并保持這樣的分離,
是往結構化走的重要一步。
基本(元素)規則和布局規則
之所以把這兩類單獨提出來,是因為這是我們平時理解的css似乎就完全只有這兩類。 我們對那些劃歸這兩類的風格沒有異議,而對那些屬于這兩類的反而不太理解。 還是先簡單了解一下這兩類本身的范圍。
基本規則
是應用于基本元素級別的規則,作用于全局統一(默認)的風格。 最好的一個案例:CSS的重置框架reset.css以及bootstrap的改進方案normalize.css, 就完全是基本風格規則,不包含任何其他類型的規則。 雖然其作用和我們平時項目中的基本風格不相同,用來理解基本風格的范疇是及其恰當的。
布局規則
在我們一開始談前端的結構化時,腦海中第一浮現的設計就是這個分層結構樹(或則分形的思維):
頁面 Page => 布局 Layout => 模塊 Module => 元素 Element 。
一個頁面由布局組成,每個布局局部由一個或多個模塊組成,一個模塊有n個元素組成,看上去簡單而完美,真正的結構化、模塊化。 然而,現實世界總是非線性的。在實際的項目中,嚴格的層次關系設計,遇到了各類“特例”需要打破這個結構。
比如,AngularJS是MVC架構,更準確一些,它是層次結構化MVC, 一個大的MVC又由其它幾個粒度更小的MVC組成, 特別是ui-router的嵌套狀態和視圖把這個結構表達的更清楚。 從設計的思維上,稱之為分形更恰當。
當需要模塊與模塊之間的通信和信息交流時,這種結構卻不能自然的支持,于是,有一個事件系統創造出來彌補這個缺陷。
之所以有這些“特例”,根本原因就是分形思維只適合在模塊這一級別,而不能往上擴展到布局和頁面界別,也不能往下擴展到元素級別。
布局就是布局,應該作為一個獨立的方面存在。
布局規則中,我們之關注組件之間的相互關系,不關心組件自身的設計,也不關心布局所在的位置。
比如,用list(ol或者ul)做布局用時:
.layout-grid{
margin: 0;
padding: 0;
list-style-type: none;
}
.layout-grid > li {
display: inline-block;
margin: 0 0 10px 10px;
}
'list-style-type'和'display'的設置,我們可以明顯看出是布局,'margin'和'padding'似乎更像基本風格規則。 然而,從使用的目的來看,它們都是用于布局的方面。
這個例子,我們可以看出對規則的劃分不是按CSS的技術特性,而是按業務特性:它們的作用,它們的“含義”。
模塊規則
這個類別含義是很明確清楚,只是強調一下,模塊可以放在布局的組件中,也可以放在另外一個模塊內部,是嵌套的,就是前面說的分形。
用class和語義標簽
我們一般都用class來定義模塊,如果需要用到標簽則只能是有語義的標簽。如heading系列:
.module > h2{
padding: ......
}
用subclass定義嵌套元素風格
如bootstrap的listgroup:
<ul class="list-group">
<li class="list-group-item">First item</li>
<li class="list-group-item">Second item</li>
<li class="list-group-item">Third item</li>
</ul>
可以看到,在list-group之外,它又另外定義了list-group-item來修飾li,而不是用以下方式省略掉子類的聲明和使用:
.list-group > li {
...
}
為什么要這樣? 可以作為一個思考題放在這。
狀態規則
狀態和子模塊有時候很相似,卻有亮的明顯區別:
0. 狀態改變布局風格或模塊風格
0. 狀態大部分時候和Javascript相聯系
這是什么意思呢,我們看看例子:
最經典的案例就是表單數據的有效性,一般都會引入class定義,類似is-valid;還有就是tab當前激活的狀態is-tab-active等。 前者,會改變表單的布局:增加warning信息;后者,會改變tab模塊的顯示背景來表明當前tab是被選中的。
而以上兩個類也都會由javascript根據用戶操作,動態的添加到相應的DOM元素中去。
從狀態規則的兩個關鍵詞:改變和javascript,我們能很明顯的看出它如其他規則的區別,仍然重點在它的用途和業務含義。 它最重要的一個業務邏輯就是:狀態規則與時間相關,這也足以給它一個獨立的地位,與模塊規則的維度呈正交關系。
正交設計的延伸閱讀:
主題規則
主題是整個網站的風格全面的改變,可以跨項目的才改一次,因而可以在編譯階段進行 如bootstrap的customize用于這種場景;還有就是在一個項目之內也容許用戶動態改變。 這些絕對是與其他規則不在一個方面,必定要獨立出來。
這類規則會涉及到所有其他類型的規則,如:基本,模塊甚至布局和狀態,雖然代碼量和工作量都較大,概念上卻很清楚,這里就不再展開。
基本規則和模塊規則的正交案例
在比較中,我們看看類別之間的區別。
場景
比如說我們網頁中需要一個表格來顯示一些信息,如iPhone 7的產品參數 https://www.apple.com/cn/iphone-7/specs/
為它寫一個簡單的風格, 沒有任何問題:
table{
width: 100%;
border-collapse: collapse;
border: 1px solid #000;
border-width: 1px 1px;
}
td{
border: 1px solid #000;
border-width: 1px 1px;
}
之后我們拿到一個新的需求,同樣用表格但是用來比較不同型號的產品的參數,如 https://www.apple.com/cn/iphone/compare/
為了給客戶更好的體驗,需要對表格風格做相應的調整,如相間隔的列用不同的背景色區分,表格的行之間需要實線間隔,而列之間則不要。 而且,這些修改不能影響之前信息表格的風格。
覆蓋方式解決表格的變體
直觀的解決方案,我們引用一個類comparison來覆蓋之前的基本規則 :
.comparision {
border-width: 0px 0;
}
.comparison tr > td:nth-child(even){
background-color: #AAA;
}
.comparison tr > td {
border-width: 1px 0;
}
完全依照新需求,做了三件事情: 1. 去標題的間隔線 2. 去掉了內容行之間的豎線間隔 3. 雙列背景灰顯。 點擊參看在線Demo。
模塊方式解決表格變體
然而,用模塊的方式更為清楚,更容易擴展。
基本風格(全局)
首先,與OO中提出基類的思維類似,這里我們也提出公共的風格部分:
table {
width: 100%;
border-collapse: collapse;
}
信息表風格類info
然后,為原來的信息表格寫出一個分支風格(OO中的子類)
.info {
border: 1px solid #000;
border-width: 1px 1px;
}
.info tr > td {
border: 1px solid #000;
border-width: 1px 1px;
}
比較表信息類comparison
最后,為新的比較表格寫出另外一個分支風格
.comparison tr > td {
border: 1px solid #666;
border-width: 1px 0;
}
.comparison tr > td:nth-child(even){
background-color: #AAA;
}
點擊參看在線Demo
比較分析
- 覆蓋的方式中,盡管也用了類
comparison,從設計的概念和使用的方式可以看到,和模塊中的comparison還是不同的,其業務的語義性弱化很多,以至于作為開發者對其命名的準確程度都不太在意了。 這是一個很不好的傾向。 - 基本風格的
width和border-collapse確確實實是全局的風格,不多一點也不少一點 - 結構非常清晰,風格之間沒有復雜的覆蓋關系: 比如比較表格中的
border-width不會像前面的實現那樣,先td{border-width: 1px 1px;}然后又.comparison tr > td {border-width: 1px 0;}覆蓋掉。 可以想象在實際項目中,更多層次的覆蓋和更多規則的引入會帶來多少的復雜度和差錯率,你怎么能準確的判斷到底是那個規則再起作用?
狀態規則和模塊規則的正交案例
因為時間問題,這個案例需要到下次再整理了。
設計思維回顧:
分形:
非常強大的思維,對它本身似乎有點陌生,當說起遞歸、全息理論是否更熟悉一些? 這些都可以看作是分形思維的應用。
面向方面編程:
有時候又稱為面向切面編程,曾經是個炙手可熱的名詞,現在好像沒怎么提起,不是不再適用而是思維已經進入常用編程思維,不再需要強調了。
正交設計
和面向方面有些雷同,在這重復也算一個強調吧。 另外,面向方面只能算正交設計的一種實現方式吧。
語義性
當你看到這“藍色 ”兩個字時, 你腦子里想到的是“藍色”還是“紅色”?
語義設計就是要讓命名和內容一致,不要扭曲人性。 提升一個層次:我們要讓代碼文檔化。
覆蓋和模塊
覆蓋是無結構,典型的“修補”編程法,甚至當不同需求被引入的前后順序不同時,會導致不同的代碼結構,隨意性太強。 模塊有設計,有業務含義,可維護性很強。
皓月碧空,漫野如洗,行往卓越的路上

浙公網安備 33010602011771號