「小白DAY4」這樣你就懂了,談CSS設計模式
作者:趙偵鎬-騰訊前端開發工程師
博客:沒那麼難,談CSS的設計模式 | 靈感的小窩@IMWeb前端社區,若未授權禁止轉載
什麼是設計模式?
曾有人調侃,設計模式是工程師用於跟別人顯擺的,顯得高大上;也曾有人這麼說,不是設計模式沒用,是你還沒有到能懂它,會用它的時候。
先來看一下比較官方的解釋:"設計模式(Design pattern)是一套被反覆使用、多數人知曉的、經過分類的、代碼設計經驗的總結。使用設計模式是為了可重用代碼、讓代碼更容易被他人理解、保證代碼可靠性。 毫無疑問,設計模式於己於他人於系統都是多贏的;設計模式使代碼編製真正工程化;設計模式是軟體工程的基石脈絡,如同大廈的結構一樣。」
今天我們來聊聊CSS的設計模式。
設計模式,這個辭彙我們常見,幾乎所有的編程語言都會有幾套,但深入研究的人不多,原因如下:
1、似乎沒有太大必要性去強調它,有問題了改一下或者按團隊規範來就行;
2、不去使用一些既有的模式也無傷大雅;3、不少人所接觸的業務量級還沒有達到需要規劃和組織的程度,光寫布局,寫特效,照顧兼容,就夠喝一壺的了,沒有意識去思考一些方法論的問題。當然,這三者都是我經歷過的,相信你也是~
我們都會長大,都會慢慢的做更多、更大、更複雜的項目,這個時候,就需要自上而下,全流程的去思考一些問題,後台不說,只講前端,比如:風格的制定、色調、模塊、布局方式、交互方式、邏輯等等,如果再加上團隊合作,若再沒有一個規劃的話,要不了多久,那些看起來沒問題的代碼,就會暴露出各種問題,模塊命名、類的命名、文件的組織、共用模塊的提取、代碼的復用、可讀性、擴展性、維護性。它們看起來只是一些簡單的小動作,卻需要你看得更遠,避免將來出問題需要付出更大的代價,甚至被迫整個項目重構,可謂,功在當代,利在千秋~
既然要對CSS進行設計,那麼肯定是它本身存在一些問題或者缺陷,其中,一個最明顯的就是,它的任何一個規則,都是全局性的聲明,會對引入它的頁面當中所有相關元素起作用,不管那是不是你想要的。而獨立及可組合的模塊是一個可維護系統的關鍵所在。下面,我們就從多個層面來探討一下,到底該怎樣寫CSS,才是更科學的。
從需求出發
分
我們剛開始學習寫字的時候,是不會去考慮,寫出來的某句話好不好,文章結構合適不合適,因為我們是意識不到的。寫代碼也一樣,剛開始,我們只是去定義規則,能用對了屬性,語法正確,把頁面實現出來了,就好。慢慢地,就會發現,頁面也是有結構的,我們按照頁面的結構去組織代碼,會不會更好?比如,分成頭部、導航、側邊欄、banner區、主內容區、底部等。然而這樣貌似還是不夠,因為還有一些東西,復用度是很高的,又不好把它歸為任何一個固有模塊,比如:麵包屑、分頁、彈窗等,它們不適合被放到某一個固有模塊的代碼中,就可以單獨的分出一段專屬的css和js,或許,這就是組件化的由來~
拆
在分了之後,我們的代碼看起來已經比之前好很多了,組織清晰,維護性大幅提高,但是,好像還是不夠,我們會發現另外一些東西,很細小,但復用度也很高,它們同樣不適合被放到模塊中去,比如,邊框、背景、圖標、字體、邊距、布局方式等等。如果我們在每個需要它們的地方,都定義一次,它們會被重複很多次,顯然,這背離好的實踐,會造成代碼冗餘和維護困難。所以,我們需要「拆」。拆過之後會怎樣?我們想在哪裡用可以直接加,需要改的時候統一改。排
經過了「分」、「拆」,我們的代碼結構已經十分清晰,各個內容模塊,功能模塊,UI模塊都乖巧的等待召喚,那麼還差什麼?是的,還差有序的組織,分類清晰之後,還需要排列有序,從不同緯度去考量,我們總能精益求精。舉個栗子,我們可能會看到像這樣:@import "mod_reset.css";
@import "ico_sprite.css";@import "mod_btns.css";@import "header.css";@import "mod_tab.css";@import "footer.css";
我們將不同的部分按照一定的順序去擺放,能讓我們的代碼看起來更加有序,易於維護,同時,有利於進行繼承或層疊覆蓋。不要小看這一步,看似可有可無,實際要求比較高的統籌規劃能力,可以減少冗餘代碼和快速定位問題位置等。
除此之外,我們依然可以有其他的方法來幫助我們進行區分代碼範圍,比如:
1、在文件頭部建立一個簡要的目錄
2、使用區塊注釋
在注釋中,應該盡量詳細的寫清楚該段代碼的目的,狀態切換,調整原因,交互邏輯等等,這樣不僅利於自己的維護,更加利於別人接手維護你的代碼。
從結論出發
除了需求當中一些通用部分,另外一些也是需要注意,但不會被正式定義的東西,它們來源於我們的實踐經驗,比如:
層級嵌套不要太深
稍微了解一些瀏覽器渲染原理的都知道,它在解析CSS規則的時候,是從右向左,一層層的去遍歷尋找,如果層級太多,必然增加了渲染時間,影響渲染速度。另外,如果選擇器層級過多,也就間接反應了,你的HTML結構可能寫得不夠簡潔。
那麼具體多少層合適?一般建議是不超過4層,但話又說回來,超過4層會怎樣嗎?不會有多明顯的影響,除非你寫到很恐怖的數量,或者項目極其龐雜,可能能看出來影響,其實從我們日常需求來看,4層以內足可以解決絕大多數問題,故而,是合理的。
避免使用元素選擇器
出於兩點考慮:
第一點,和上一段提到的相關,在HTML中,有很多常用的高頻元素,比如,div、p、span、a、ul等,如果,你在多層選擇器的最內層使用了元素選擇器,那麼,在開始尋找時,瀏覽器就會遍歷HTML中的所有該元素,顯然,這是沒有必要的。
第二點,我們的需求和代碼結構都是存在著潛在變化的,今天寫好了一個頁面,明天可能就要再加進去一個按鈕,再加進去一句話,再加進去一個圖標。我們寫好的一個結構,也隨時可能被複用到別的結構中去,所以,如果,你使用了元素選擇器去定死某個東西,不論是新加進來的東西,還是被複用的東西加到別的結構里去,都極有可能產生樣式的衝突,這個時候,你又不得不寫多餘的樣式進行覆蓋修正,或者重新定義類。
所以,出於以上考慮,在具體的代碼模塊中,盡量不要使用元素選擇器,使用元素選擇器的前提是,你完全的確定,不會導致出現問題。注意,我用的限定範圍是「具體的代碼模塊」,那麼用於定義通用規則的樣式,是可以的,也是推薦使用的,比如,reset。也可以是別的地方,這就需要我們自行考量。
避免使用群組選擇器
群組選擇器會有什麼問題?直接上圖吧。
圖中這種情況不多見,此處只是舉個例子,這裡寫了三組選擇器,用來定義不同地方的同一種樣式,其明顯的缺陷是,如果有第四個地方需要使用到,你不得不再往裡加一組選擇器,如果有10個不同的地方,你就寫10個?這對於維護來說,是很痛苦的,聰明的我們,怎能被如此繁複又不必要的勞動所困擾,故而,牆裂不推薦此種做法,完全可以提取出來一個公用類,定義統一樣式,然後,哪裡需要放哪裡,復用和維護都會更加方便。
當然,你可能會說,我在寫第一個的時候,不會知道後面還有那麼多,有沒有必要提取是不知道的,是的,所以,需要你根據經驗去判斷,也需要在項目推進過程中,適時的對代碼進行整理和重構。
文件引入的數量和順序
對於剛接觸網頁的朋友來說,這兩點也是容易忽視的,因為它們看起來沒什麼大影響,多幾次請求,樣式是否已經載入,都沒那麼容易把人逼瘋,但是出於對用戶體驗的極致追求,我們還是希望文件請求次數盡量少,內容的顯示有個優先順序,文件載入有個先後順序,這樣,在實在難以縮減文件大小的時候,讓用戶先看到更重要的,正常展示的內容。
以上只是幾點舉例,更多實戰結論,大家可以多讀相關的博文或者書籍,都會有前輩們的經驗之談。
從矛盾出發
通用和語義
Naming convention is beneficial for immediately understanding which category a particular style belongs to and its role within the overall scope of the page. On large projects, it is more likely to have styles broken up across multiple files. In these cases, naming convention also makes it easier to find which file a style belongs to.
命名規則有助於立即理解一個特定樣式屬於哪一類,它在頁面的整體範圍內的作用。在大型項目中,它更可能有在多個文件中被打破的樣式。在這種情況下,命名約定也可以更容易地找到一個樣式屬於哪個文件的文件。
很多時候,我們需要一個東西被定義為通用的,以便復用,比如:模塊標題、按鈕、提示文字、圖標等,最開始的時候,我們習慣去看視覺稿的內容,是「新聞」,我們就定義「new」,是「關於」,我們就定義「about」,是紅色的按鈕,我們就定義「red-btn」等,這樣會導致一個問題,如果有另外一個跟新聞列表差不多的樣式和結構,但不是新聞,怎麼辦?繼續使用「new」顯然不合適,這就告訴我們,不能把目光局限於內容,需要內容和結構分離。
不能用「new」了,那用什麼呢?abc?123?這樣總不會衝突了吧,萬事大吉~其實,這是走了另一個極端,這樣雖然在很大程度上避免了和別的模塊衝突,但其本身的可讀性就被大大降低了,別人,甚至你自己過一段時間都會忘記它是什麼,對於團隊合作是很不利的。至於需要用什麼樣的命名方式,需要你根據項目的整體來進行規劃,適合根據什麼特點來區分與之不同的結構,又能讓人比較容易的在名稱和結構之間建立聯繫,比如所屬類別、功能、頁面等。
團隊和個人
一個團隊當中,大家的經歷不同,編碼水平和習慣也不同,這樣就會造成,一個人一個寫法,你用中劃線,我用下劃線;我用英文全拼,你用簡寫,等等。這些雖然沒有什麼對錯之分,但對於團隊成員之間的協作造成了不小的障礙,別人必須花時間去適應和讀懂你是怎樣組織和定義的,這就無形之中提高了成本。
所以,就有了「團隊規範」存在的必要,規範除了一些寫法上的規定,讓我們的代碼更加統一,清晰,可讀性更強,辨識度更高。還可以提取一些最佳實踐和復用模塊等,對於團隊里每個人來說,都是有好處的。
當然,對於人來說,最難的,莫過於調整既有的習慣,這就會有進入一個團隊之後「轉型」的陣痛,其實這種痛也是成長的痛,你會學習到更好的編碼方式,更好的實踐方法,會從項目或者團隊的整體去考量一件事的價值和意義。
CSS和預處理器
前面我有文章詳細的談過CSS預處理器,我曾經對它也是排斥的,因為學習成本,因為覺得應用起來沒有必要,可是一旦你決定去學習使用它,就會覺得不是那樣,預處理器在向你介紹它自己的時候,就有特意強調過,它的語法是和CSS完全兼容的,也就是說,你在LESS或者SASS文件中,直接寫CSS代碼是沒有問題的。除此之外,它能給我們提供很多便利,比如定義統一的變數;使用嵌套而不用一直重複著寫一些選擇器;可以提取公共的代碼塊然後很方便的復用等等。
故而,當我們已經把CSS組織和書寫得很好了之後,預處理器,就是再次為我們插上一雙翅膀,能更靈活和高效的編碼。
從現有模式出發
再來簡單看看一些廣為流傳的模式。(ps:先後順序與排名、好壞無關)
一、OOCSS——Object Oriented CSS
接觸過計算機的應該都知道,OOP——Object Oriented Programming,如果你是第一次接觸OOCSS,你會很困惑,難道是「面向對象的CSS」嗎?它不是一本真正的編程語言啊,如何面向對象?OOCSS,最早被提及,是在2009年,它的兩大原則是:
separating structure from skin and container from content.
直譯過來就是,結構和皮膚分離,容器和內容分離。
即不要把結構和皮膚以及內容進行強耦合,而是相互獨立,所要達到的目標是更易復用和組合,可以選擇使用,選擇引用等。
詳細了解鏈接:An Introduction To Object Oriented CSS (OOCSS) – Smashing Magazine
二、SMACSS——Scalable and Modular Architecture for CSS
從實踐上說,OOCSS給出了一種值得借鑒的思想,但在代碼的組織方面,它並未給出具體的實施方法,從這一點上來說,SMACSS更進一步。
它的核心是:
1、Base(基礎)
基礎的樣式,就是一些需要最先定義好,針對於某一類元素的通用固定樣式。2、Layout(布局)
布局樣式,是跟頁面整體結構相關,譬如,列表,主內容,側邊欄的位置、寬高、布局方式等。3、Module(模塊)
模塊樣式,就是我們在對頁面進行拆的過程中,所抽取分類的模塊,這類的樣式分別寫到一起。4、State(狀態)
頁面中的某些元素會需要響應不同的狀態,比如,可用、不可用、已用、過期、警告等等。將這類樣式可以組織到一起。5、Theme(主題)
主題是指版面整個的顏色、風格之類,一般網站不會有頻繁的較大的改動,給我們印象比較深的是QQ空間,其他應用的不是很多,所以,這個一般不會用到,但有這樣一個意識是好的,需要用到的時候,就知道該怎樣規劃。有了以上5點分類策略,我們的代碼組織起來,思路就會很清晰,會安排的很有序,另外的好處是,可以解決命名難和混亂,之所以有這個問題,主因便是我們不知道以怎樣的標準去定義元素的所屬和特點,有了分類之後,我們不會很隨意和混亂的去命名,有了依據,就能更輕鬆,也不易衝突。
詳細了解鏈接:Scalable and Modular Architecture for CSS
三、Meta CSS
原子類,也可以稱之為「無語義」類,像這樣:
它的特點是什麼?樣式和結構、內容無關,預先定義好這麼一組規則,在需要的地方加上即可,我相信每個人第一次看到這種寫法的時候,都會想:還能這樣寫啊?!是的,總有一些人,一些新的思想和方法會湧現出來,它就是其中之一,當然,並不是在稱讚其本身有多麼好,而是說這種現象和過程是好的,它本身經常被人吐槽,比如:「這樣寫和直接內聯有區別嗎?」、「如果要調整樣式,就要去改HTML,維護更加麻煩,也有違樣式和結構分離的初衷」等等,其實我個人也是不贊成上面這種寫法的,如果你要把這些抽離出來,那麼還有什麼抽不出來的呢?而且這些屬性,在項目之間,頁面之間,模塊之間,並沒有太大的通用性,把這些抽出來,只不過是稍微懶省勁兒些,但為了照顧到更多情況,你必須寫入冗餘代碼,是得不償失的。
雖然它有缺點,我個人贊成另外的一些東西分出來,比如:浮動(float)、文本布局(text-align)、Flexbox布局等,這些是沒有那麼多可能性的值,而且使用頻繁,復用方便,改動較少,除此之外,你還可以提取另外一些公共的小顆粒類,比如按鈕的種類,文字顏色的種類等,這些和CSS本身無關,和項目相關,這就是借鑒其思想,而不是直接拿來用。
四、BEM
嚴格說來,BEM不是一套有骨有肉的模式,也不僅僅局限你在CSS的層面去規劃,它是一種怎樣去組織、編寫代碼的思想,而且,看似簡單的它,對前端界的影響卻是巨大的。
它的核心如下:
Block(塊)、Element(元素)、Modifier(修飾符)
它幫助我們定義頁面中每一部分的級別屬性,從某種意義上說,也是一種「拆」。命名規則如下:
它的出現,曾給不少人帶來啟發,但是也有另一部分人仍然抱著挑剔的態度,譬如:
1、風格不統一,顯得代碼不夠整潔美觀
2、可能會導致類名過長還是前面說的,你可以不去直接用它,但要清楚它的優點:能夠使得我們僅通過類名就知道哪些代碼是屬於一個模塊內,以及在模塊中所起的作用。然後借鑒之。
當然,BEM集聚了很多人的心血,也得到了很多的讚譽,其中就包括OOCSS的作者。所以,它肯定不是這麼簡單。它還會告訴你,怎樣配合著js來寫,你的文件怎樣組織更好,項目該怎麼構建等。詳細可以到官網去查閱。地址:BEM
從實際出發,決定結果的人是你
到底怎樣使用設計模式?
雖然已經有成熟的設計模式,但在實際當中,你可能覺得哪個跟自己的項目都不能完全吻合,或者你要去為了使用它們而調整,成本很高。其實,我們不需要去迎合模式,要讓模式為我所用,你需要去了解它們背後的原理,要知道它們用什麼方式解決了什麼問題,然後借鑒之,用它的方式解決我們的問題,就好,這樣就不需要作難要不要用,也不需要糾結選哪個,不是簡單的說哪個好,哪個不好,總有我們能夠用得上它的地方。海納百川,集百家眾長。
我個人一直以來所堅持的另一個觀點就是,前端開發的三駕馬車——html、css、js,不要,也不能孤立的去談那樣好或者這樣好,我們極少會有只用一次的代碼或者模塊,也不會只寫一種語言,三駕馬車都是在一起協作的,都是會有復用、擴展和團隊合作多方面的因素在裡面,故而,不能抱著這樣的想法:我現在就在做這個,它就是唯一的,就是固定的,沒問題。其實很多問題都是潛在的,要帶著發展眼光去看待。項目的文件之間,項目之間,團隊成員之間,不論你的分工是哪塊,都要考慮到前後的影響和可能給合作帶來的不便。
怎樣才是最佳實踐?有「實踐」,才有「最佳」,脫離實際情況談最佳,就是空中樓閣。那麼,最好的模式,不是哪個經典的模式,而是在項目進行中,不斷的磨合調整而出的。故而,不需要再懼怕看起來不明覺厲的設計模式,也不需要因為自己還不懂設計模式而鬱悶,它就是人們總結出來的實戰方法,你也可以有自己的模式~
若你閱讀該文中有什麼疑問,歡迎在下方評論區留言,我們會選擇部分問題解答
推薦閱讀:
※自己實現的觀察者模式、BroadcastReceiver和EventBus三者的優缺點是什麼?
※MVC 架構與 Observer 模式有什麼異同點?
※c++ 單例模式的一點疑問,求解答?
※在替考的代理模式中到底誰是Proxy?
※怎樣才能在寫代碼時沒有一種「如履薄冰」的感覺?